Hardware/LPC/IT87XX.cs
author sl
Sun, 03 Feb 2013 18:01:50 +0100
changeset 391 ca4c0e7ae75d
parent 353 b4e37f5b2669
permissions -rw-r--r--
Converted project to VisualStudio 2012.
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
     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-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System.Globalization;
    12 using System.Text;
    13 using System;
    14 
    15 namespace OpenHardwareMonitor.Hardware.LPC {
    16   internal class IT87XX : ISuperIO {
    17        
    18     private readonly ushort address;
    19     private readonly Chip chip;
    20     private readonly byte version;
    21 
    22     private readonly ushort gpioAddress;
    23     private readonly int gpioCount;
    24 
    25     private readonly ushort addressReg;
    26     private readonly ushort dataReg;
    27 
    28     private readonly float?[] voltages = new float?[0];
    29     private readonly float?[] temperatures = new float?[0];
    30     private readonly float?[] fans = new float?[0];
    31     private readonly float?[] controls = new float?[0];
    32 
    33     private readonly float voltageGain;
    34     private readonly bool has16bitFanCounter;
    35    
    36     // Consts
    37     private const byte ITE_VENDOR_ID = 0x90;
    38        
    39     // Environment Controller
    40     private const byte ADDRESS_REGISTER_OFFSET = 0x05;
    41     private const byte DATA_REGISTER_OFFSET = 0x06;
    42 
    43     // Environment Controller Registers    
    44     private const byte CONFIGURATION_REGISTER = 0x00;
    45     private const byte TEMPERATURE_BASE_REG = 0x29;
    46     private const byte VENDOR_ID_REGISTER = 0x58;
    47     private const byte FAN_TACHOMETER_DIVISOR_REGISTER = 0x0B;
    48     private readonly byte[] FAN_TACHOMETER_REG = 
    49       { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
    50     private readonly byte[] FAN_TACHOMETER_EXT_REG =
    51       { 0x18, 0x19, 0x1a, 0x81, 0x83 };
    52     private const byte VOLTAGE_BASE_REG = 0x20;
    53     private readonly byte[] FAN_PWM_CTRL_REG = { 0x15, 0x16, 0x17 };
    54 
    55     private bool[] restoreDefaultFanPwmControlRequired = new bool[3];       
    56     private byte[] initialFanPwmControl = new byte[3];
    57 
    58     private byte ReadByte(byte register, out bool valid) {
    59       Ring0.WriteIoPort(addressReg, register);
    60       byte value = Ring0.ReadIoPort(dataReg);
    61       valid = register == Ring0.ReadIoPort(addressReg);
    62       return value;
    63     }
    64 
    65     private bool WriteByte(byte register, byte value) {
    66       Ring0.WriteIoPort(addressReg, register);
    67       Ring0.WriteIoPort(dataReg, value);
    68       return register == Ring0.ReadIoPort(addressReg); 
    69     }
    70 
    71     public byte? ReadGPIO(int index) {
    72       if (index >= gpioCount)
    73         return null;
    74 
    75       return Ring0.ReadIoPort((ushort)(gpioAddress + index));
    76     }
    77 
    78     public void WriteGPIO(int index, byte value) {
    79       if (index >= gpioCount)
    80         return;
    81 
    82       Ring0.WriteIoPort((ushort)(gpioAddress + index), value);
    83     } 
    84 
    85     private void SaveDefaultFanPwmControl(int index) {
    86       bool valid;
    87       if (!restoreDefaultFanPwmControlRequired[index]) {
    88         initialFanPwmControl[index] = 
    89           ReadByte(FAN_PWM_CTRL_REG[index], out valid);
    90         restoreDefaultFanPwmControlRequired[index] = true;
    91       }
    92     }
    93 
    94     private void RestoreDefaultFanPwmControl(int index) {
    95       if (restoreDefaultFanPwmControlRequired[index]) {
    96         WriteByte(FAN_PWM_CTRL_REG[index], initialFanPwmControl[index]);
    97         restoreDefaultFanPwmControlRequired[index] = false;
    98       }
    99     }
   100 
   101     public void SetControl(int index, byte? value) {
   102       if (index < 0 || index >= controls.Length)
   103         throw new ArgumentOutOfRangeException("index");
   104 
   105       if (!Ring0.WaitIsaBusMutex(10))
   106         return;
   107 
   108       if (value.HasValue) {
   109         SaveDefaultFanPwmControl(index);
   110 
   111         // set output value
   112         WriteByte(FAN_PWM_CTRL_REG[index], (byte)(value.Value >> 1));  
   113       } else {
   114         RestoreDefaultFanPwmControl(index);
   115       }
   116 
   117       Ring0.ReleaseIsaBusMutex();
   118     } 
   119 
   120     public IT87XX(Chip chip, ushort address, ushort gpioAddress, byte version) {
   121 
   122       this.address = address;
   123       this.chip = chip;
   124       this.version = version;
   125       this.addressReg = (ushort)(address + ADDRESS_REGISTER_OFFSET);
   126       this.dataReg = (ushort)(address + DATA_REGISTER_OFFSET);
   127       this.gpioAddress = gpioAddress;
   128 
   129       // Check vendor id
   130       bool valid;
   131       byte vendorId = ReadByte(VENDOR_ID_REGISTER, out valid);       
   132       if (!valid || vendorId != ITE_VENDOR_ID)
   133         return;
   134 
   135       // Bit 0x10 of the configuration register should always be 1
   136       if ((ReadByte(CONFIGURATION_REGISTER, out valid) & 0x10) == 0)
   137         return;
   138       if (!valid)
   139         return;
   140 
   141       voltages = new float?[9];
   142       temperatures = new float?[3];
   143       fans = new float?[chip == Chip.IT8705F ? 3 : 5];
   144       controls = new float?[3];
   145 
   146       // IT8721F, IT8728F and IT8772E use a 12mV resultion ADC, all others 16mV
   147       if (chip == Chip.IT8721F || chip == Chip.IT8728F || chip == Chip.IT8771E 
   148         || chip == Chip.IT8772E) 
   149       {
   150         voltageGain = 0.012f;
   151       } else {
   152         voltageGain = 0.016f;        
   153       }
   154 
   155       // older IT8705F and IT8721F revisions do not have 16-bit fan counters
   156       if ((chip == Chip.IT8705F && version < 3) || 
   157           (chip == Chip.IT8712F && version < 8)) 
   158       {
   159         has16bitFanCounter = false;
   160       } else {
   161         has16bitFanCounter = true;
   162       }
   163 
   164       // Set the number of GPIO sets
   165       switch (chip) {
   166         case Chip.IT8712F:
   167         case Chip.IT8716F:
   168         case Chip.IT8718F:
   169         case Chip.IT8726F:
   170           gpioCount = 5;
   171           break;
   172         case Chip.IT8720F:
   173         case Chip.IT8721F:
   174           gpioCount = 8;
   175           break;
   176         case Chip.IT8705F: 
   177         case Chip.IT8728F:
   178         case Chip.IT8771E:
   179         case Chip.IT8772E:
   180           gpioCount = 0;
   181           break;
   182       }
   183     }
   184 
   185     public Chip Chip { get { return chip; } }
   186     public float?[] Voltages { get { return voltages; } }
   187     public float?[] Temperatures { get { return temperatures; } }
   188     public float?[] Fans { get { return fans; } }
   189     public float?[] Controls { get { return controls; } }
   190 
   191     public string GetReport() {
   192       StringBuilder r = new StringBuilder();
   193 
   194       r.AppendLine("LPC " + this.GetType().Name);
   195       r.AppendLine();
   196       r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
   197       r.Append("Chip Version: 0x"); r.AppendLine(
   198         version.ToString("X", CultureInfo.InvariantCulture));
   199       r.Append("Base Address: 0x"); r.AppendLine(
   200         address.ToString("X4", CultureInfo.InvariantCulture));
   201       r.Append("GPIO Address: 0x"); r.AppendLine(
   202         gpioAddress.ToString("X4", CultureInfo.InvariantCulture));
   203       r.AppendLine();
   204 
   205       if (!Ring0.WaitIsaBusMutex(100))
   206         return r.ToString();
   207 
   208       r.AppendLine("Environment Controller Registers");
   209       r.AppendLine();
   210       r.AppendLine("      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   211       r.AppendLine();
   212       for (int i = 0; i <= 0xA; i++) {
   213         r.Append(" "); 
   214         r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture)); 
   215         r.Append("  ");
   216         for (int j = 0; j <= 0xF; j++) {
   217           r.Append(" ");
   218           bool valid;
   219           byte value = ReadByte((byte)((i << 4) | j), out valid);
   220           r.Append(
   221             valid ? value.ToString("X2", CultureInfo.InvariantCulture) : "??");
   222         }
   223         r.AppendLine();
   224       }
   225       r.AppendLine();
   226 
   227       r.AppendLine("GPIO Registers");
   228       r.AppendLine();
   229       for (int i = 0; i < gpioCount; i++) {
   230         r.Append(" ");
   231         r.Append(ReadGPIO(i).Value.ToString("X2",
   232           CultureInfo.InvariantCulture));
   233       }
   234       r.AppendLine();
   235       r.AppendLine();
   236 
   237       Ring0.ReleaseIsaBusMutex();
   238 
   239       return r.ToString();
   240     }
   241 
   242     public void Update() {
   243       if (!Ring0.WaitIsaBusMutex(10))
   244         return;
   245 
   246       for (int i = 0; i < voltages.Length; i++) {
   247         bool valid;
   248         
   249         float value = 
   250           voltageGain * ReadByte((byte)(VOLTAGE_BASE_REG + i), out valid);   
   251 
   252         if (!valid)
   253           continue;
   254         if (value > 0)
   255           voltages[i] = value;  
   256         else
   257           voltages[i] = null;
   258       }
   259 
   260       for (int i = 0; i < temperatures.Length; i++) {
   261         bool valid;
   262         sbyte value = (sbyte)ReadByte(
   263           (byte)(TEMPERATURE_BASE_REG + i), out valid);
   264         if (!valid)
   265           continue;
   266 
   267         if (value < sbyte.MaxValue && value > 0)
   268           temperatures[i] = value;
   269         else
   270           temperatures[i] = null;       
   271       }
   272 
   273       if (has16bitFanCounter) {
   274         for (int i = 0; i < fans.Length; i++) {
   275           bool valid;
   276           int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
   277           if (!valid)
   278             continue;
   279           value |= ReadByte(FAN_TACHOMETER_EXT_REG[i], out valid) << 8;
   280           if (!valid)
   281             continue;
   282 
   283           if (value > 0x3f) {
   284             fans[i] = (value < 0xffff) ? 1.35e6f / (value * 2) : 0;
   285           } else {
   286             fans[i] = null;
   287           }
   288         }
   289       } else {
   290         for (int i = 0; i < fans.Length; i++) {
   291           bool valid;
   292           int value = ReadByte(FAN_TACHOMETER_REG[i], out valid);
   293           if (!valid)
   294             continue;
   295 
   296           int divisor = 2;
   297           if (i < 2) {
   298             int divisors = ReadByte(FAN_TACHOMETER_DIVISOR_REGISTER, out valid);
   299             if (!valid)
   300               continue;
   301             divisor = 1 << ((divisors >> (3 * i)) & 0x7);
   302           }
   303 
   304           if (value > 0) {
   305             fans[i] = (value < 0xff) ? 1.35e6f / (value * divisor) : 0;
   306           } else {
   307             fans[i] = null;
   308           }
   309         }
   310       }
   311 
   312       for (int i = 0; i < controls.Length; i++) {
   313         bool valid;
   314         byte value = ReadByte(FAN_PWM_CTRL_REG[i], out valid);
   315         if (!valid)
   316           continue;
   317 
   318         if ((value & 0x80) > 0) {
   319           // automatic operation (value can't be read)
   320           controls[i] = null;  
   321         } else {
   322           // software operation
   323           controls[i] = (float)Math.Round((value & 0x7F) * 100.0f / 0x7F);
   324         }
   325       }
   326 
   327       Ring0.ReleaseIsaBusMutex();
   328     }
   329   } 
   330 }