Hardware/LPC/LPCIO.cs
author moel.mich
Tue, 30 Dec 2014 21:04:54 +0000
changeset 430 6b24e39f1b84
parent 413 362c5e77197d
child 454 f84878f52cd9
permissions -rw-r--r--
Fixed Issue 651.
     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-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Collections.Generic;
    13 using System.Globalization;
    14 using System.Text;
    15 using System.Threading;
    16 
    17 namespace OpenHardwareMonitor.Hardware.LPC {
    18   internal class LPCIO {
    19 
    20     private readonly List<ISuperIO> superIOs = new List<ISuperIO>();
    21     private readonly StringBuilder report = new StringBuilder();
    22 
    23     // I/O Ports
    24     private readonly ushort[] REGISTER_PORTS = new ushort[] { 0x2E, 0x4E };
    25     private readonly ushort[] VALUE_PORTS = new ushort[] { 0x2F, 0x4F };
    26 
    27     private ushort registerPort;
    28     private ushort valuePort;
    29 
    30     // Registers
    31     private const byte CONFIGURATION_CONTROL_REGISTER = 0x02;
    32     private const byte DEVCIE_SELECT_REGISTER = 0x07;
    33     private const byte CHIP_ID_REGISTER = 0x20;
    34     private const byte CHIP_REVISION_REGISTER = 0x21;
    35     private const byte BASE_ADDRESS_REGISTER = 0x60;
    36 
    37     private byte ReadByte(byte register) {
    38       Ring0.WriteIoPort(registerPort, register);
    39       return Ring0.ReadIoPort(valuePort);
    40     }
    41 
    42     private void WriteByte(byte register, byte value) {
    43       Ring0.WriteIoPort(registerPort, register);
    44       Ring0.WriteIoPort(valuePort, value);
    45     }
    46 
    47     private ushort ReadWord(byte register) {
    48       return (ushort)((ReadByte(register) << 8) |
    49         ReadByte((byte)(register + 1)));
    50     }
    51 
    52     private void Select(byte logicalDeviceNumber) {
    53       Ring0.WriteIoPort(registerPort, DEVCIE_SELECT_REGISTER);
    54       Ring0.WriteIoPort(valuePort, logicalDeviceNumber);
    55     }
    56 
    57     private void ReportUnknownChip(string type, int chip) {
    58       report.Append("Chip ID: Unknown ");
    59       report.Append(type);
    60       report.Append(" with ID 0x");
    61       report.Append(chip.ToString("X", CultureInfo.InvariantCulture));
    62       report.Append(" at 0x");
    63       report.Append(registerPort.ToString("X", CultureInfo.InvariantCulture));
    64       report.Append("/0x");
    65       report.AppendLine(valuePort.ToString("X", CultureInfo.InvariantCulture));
    66       report.AppendLine();
    67     }
    68 
    69     #region Winbond, Nuvoton, Fintek
    70 
    71     private const byte FINTEK_VENDOR_ID_REGISTER = 0x23;
    72     private const ushort FINTEK_VENDOR_ID = 0x1934;
    73 
    74     private const byte WINBOND_NUVOTON_HARDWARE_MONITOR_LDN = 0x0B;
    75 
    76     private const byte F71858_HARDWARE_MONITOR_LDN = 0x02;
    77     private const byte FINTEK_HARDWARE_MONITOR_LDN = 0x04;
    78 
    79     private const byte NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK = 0x28;
    80 
    81     private void WinbondNuvotonFintekEnter() {
    82       Ring0.WriteIoPort(registerPort, 0x87);
    83       Ring0.WriteIoPort(registerPort, 0x87);
    84     }
    85 
    86     private void WinbondNuvotonFintekExit() {
    87       Ring0.WriteIoPort(registerPort, 0xAA);
    88     }
    89 
    90     private bool DetectWinbondFintek() {
    91       WinbondNuvotonFintekEnter();
    92 
    93       byte logicalDeviceNumber = 0;
    94       byte id = ReadByte(CHIP_ID_REGISTER);
    95       byte revision = ReadByte(CHIP_REVISION_REGISTER);
    96       Chip chip = Chip.Unknown;
    97       switch (id) {
    98         case 0x05:
    99           switch (revision) {
   100             case 0x07:
   101               chip = Chip.F71858;
   102               logicalDeviceNumber = F71858_HARDWARE_MONITOR_LDN;
   103               break;
   104             case 0x41:
   105               chip = Chip.F71882;
   106               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   107               break;
   108           } break;
   109         case 0x06:
   110           switch (revision) {
   111             case 0x01:
   112               chip = Chip.F71862;
   113               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   114               break;
   115           } break;
   116         case 0x07:
   117           switch (revision) {
   118             case 0x23:
   119               chip = Chip.F71889F;
   120               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   121               break;
   122           } break;
   123         case 0x08:
   124           switch (revision) {
   125             case 0x14:
   126               chip = Chip.F71869;
   127               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   128               break;
   129           } break;
   130         case 0x09:
   131           switch (revision) {
   132             case 0x01:
   133               chip = Chip.F71808E;
   134               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   135               break;
   136             case 0x09:
   137               chip = Chip.F71889ED;
   138               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   139               break;
   140           } break;
   141         case 0x10:
   142           switch (revision) {
   143             case 0x05:
   144               chip = Chip.F71889AD;
   145               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   146               break;
   147             case 0x07:
   148               chip = Chip.F71869A;
   149               logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
   150               break;
   151           } break;
   152         case 0x52:
   153           switch (revision) {
   154             case 0x17:
   155             case 0x3A:
   156             case 0x41:
   157               chip = Chip.W83627HF;
   158               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   159               break;
   160           } break;
   161         case 0x82:
   162           switch (revision & 0xF0) {
   163             case 0x80:
   164               chip = Chip.W83627THF;
   165               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   166               break;
   167           } break;
   168         case 0x85:
   169           switch (revision) {
   170             case 0x41:
   171               chip = Chip.W83687THF;
   172               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   173               break;
   174           } break;
   175         case 0x88:
   176           switch (revision & 0xF0) {
   177             case 0x50:
   178             case 0x60:
   179               chip = Chip.W83627EHF;
   180               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   181               break;
   182           } break;
   183         case 0xA0:
   184           switch (revision & 0xF0) {
   185             case 0x20:
   186               chip = Chip.W83627DHG;
   187               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   188               break;
   189           } break;
   190         case 0xA5:
   191           switch (revision & 0xF0) {
   192             case 0x10:
   193               chip = Chip.W83667HG;
   194               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   195               break;
   196           } break;
   197         case 0xB0:
   198           switch (revision & 0xF0) {
   199             case 0x70:
   200               chip = Chip.W83627DHGP;
   201               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   202               break;
   203           } break;
   204         case 0xB3:
   205           switch (revision & 0xF0) {
   206             case 0x50:
   207               chip = Chip.W83667HGB;
   208               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   209               break;
   210           } break;
   211         case 0xB4:
   212           switch (revision & 0xF0) {
   213             case 0x70:
   214               chip = Chip.NCT6771F;
   215               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   216               break;
   217           } break;
   218         case 0xC3:
   219           switch (revision & 0xF0) {
   220             case 0x30:
   221               chip = Chip.NCT6776F;
   222               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   223               break;
   224           } break;
   225         case 0xC5:
   226           switch (revision & 0xF0) {
   227             case 0x60:
   228               chip = Chip.NCT6779D;
   229               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   230               break;
   231           } break;
   232         case 0xC8:
   233           switch (revision) {
   234             case 0x03:
   235               chip = Chip.NCT6791D;
   236               logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
   237               break;
   238           } break;
   239       }
   240       if (chip == Chip.Unknown) {
   241         if (id != 0 && id != 0xff) {
   242           WinbondNuvotonFintekExit();
   243 
   244           ReportUnknownChip("Winbond / Nuvoton / Fintek", 
   245             ((id << 8) | revision));
   246         }
   247       } else {
   248 
   249         Select(logicalDeviceNumber);
   250         ushort address = ReadWord(BASE_ADDRESS_REGISTER);
   251         Thread.Sleep(1);
   252         ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
   253 
   254         ushort vendorID = ReadWord(FINTEK_VENDOR_ID_REGISTER);
   255 
   256         // disable the hardware monitor i/o space lock on NCT6791D chips
   257         if (address == verify && chip == Chip.NCT6791D) {
   258           byte options = ReadByte(NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK);
   259 
   260           // if the i/o space lock is enabled
   261           if ((options & 0x10) > 0) {
   262 
   263             // disable the i/o space lock
   264             WriteByte(NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK,
   265               (byte)(options & ~0x10));
   266           }
   267         }
   268 
   269         WinbondNuvotonFintekExit();
   270 
   271         if (address != verify) {
   272           report.Append("Chip ID: 0x");
   273           report.AppendLine(chip.ToString("X"));
   274           report.Append("Chip revision: 0x");
   275           report.AppendLine(revision.ToString("X",
   276             CultureInfo.InvariantCulture));
   277           report.AppendLine("Error: Address verification failed");
   278           report.AppendLine();
   279           return false;
   280         }
   281 
   282         // some Fintek chips have address register offset 0x05 added already
   283         if ((address & 0x07) == 0x05)
   284           address &= 0xFFF8;
   285 
   286         if (address < 0x100 || (address & 0xF007) != 0) {
   287           report.Append("Chip ID: 0x");
   288           report.AppendLine(chip.ToString("X"));
   289           report.Append("Chip revision: 0x");
   290           report.AppendLine(revision.ToString("X",
   291             CultureInfo.InvariantCulture));
   292           report.Append("Error: Invalid address 0x");
   293           report.AppendLine(address.ToString("X",
   294             CultureInfo.InvariantCulture));
   295           report.AppendLine();
   296           return false;
   297         }
   298 
   299         switch (chip) {
   300           case Chip.W83627DHG:
   301           case Chip.W83627DHGP:
   302           case Chip.W83627EHF:
   303           case Chip.W83627HF:
   304           case Chip.W83627THF:
   305           case Chip.W83667HG:
   306           case Chip.W83667HGB:
   307           case Chip.W83687THF:
   308             superIOs.Add(new W836XX(chip, revision, address));
   309             break;
   310           case Chip.NCT6771F:
   311           case Chip.NCT6776F:
   312           case Chip.NCT6779D:
   313           case Chip.NCT6791D:
   314             superIOs.Add(new NCT677X(chip, revision, address));
   315             break;
   316           case Chip.F71858:
   317           case Chip.F71862:
   318           case Chip.F71869:
   319           case Chip.F71869A:
   320           case Chip.F71882:
   321           case Chip.F71889AD:
   322           case Chip.F71889ED:
   323           case Chip.F71889F:
   324           case Chip.F71808E:
   325             if (vendorID != FINTEK_VENDOR_ID) {
   326               report.Append("Chip ID: 0x");
   327               report.AppendLine(chip.ToString("X"));
   328               report.Append("Chip revision: 0x");
   329               report.AppendLine(revision.ToString("X",
   330                 CultureInfo.InvariantCulture));
   331               report.Append("Error: Invalid vendor ID 0x");
   332               report.AppendLine(vendorID.ToString("X",
   333                 CultureInfo.InvariantCulture));
   334               report.AppendLine();
   335               return false;
   336             }
   337             superIOs.Add(new F718XX(chip, address));
   338             break;
   339           default: break;
   340         }
   341 
   342         return true;
   343       }
   344 
   345       return false;
   346     }
   347 
   348     #endregion
   349 
   350     #region ITE
   351 
   352     private const byte IT87_ENVIRONMENT_CONTROLLER_LDN = 0x04;
   353     private const byte IT8705_GPIO_LDN = 0x05;
   354     private const byte IT87XX_GPIO_LDN = 0x07;
   355     private const byte IT87_CHIP_VERSION_REGISTER = 0x22;
   356 
   357     private void IT87Enter() {
   358       Ring0.WriteIoPort(registerPort, 0x87);
   359       Ring0.WriteIoPort(registerPort, 0x01);
   360       Ring0.WriteIoPort(registerPort, 0x55);
   361       Ring0.WriteIoPort(registerPort, 0x55);
   362     }
   363 
   364     private void IT87Exit() {
   365       Ring0.WriteIoPort(registerPort, CONFIGURATION_CONTROL_REGISTER);
   366       Ring0.WriteIoPort(valuePort, 0x02);
   367     }
   368 
   369     private bool DetectIT87() {
   370 
   371       // IT87XX can enter only on port 0x2E
   372       if (registerPort != 0x2E)
   373         return false;
   374 
   375       IT87Enter();
   376 
   377       ushort chipID = ReadWord(CHIP_ID_REGISTER);
   378       Chip chip;
   379       switch (chipID) {
   380         case 0x8705: chip = Chip.IT8705F; break;
   381         case 0x8712: chip = Chip.IT8712F; break;
   382         case 0x8716: chip = Chip.IT8716F; break;
   383         case 0x8718: chip = Chip.IT8718F; break;
   384         case 0x8720: chip = Chip.IT8720F; break;
   385         case 0x8721: chip = Chip.IT8721F; break;
   386         case 0x8726: chip = Chip.IT8726F; break;
   387         case 0x8728: chip = Chip.IT8728F; break;
   388         case 0x8771: chip = Chip.IT8771E; break;
   389         case 0x8772: chip = Chip.IT8772E; break;
   390         default: chip = Chip.Unknown; break;
   391       }
   392       if (chip == Chip.Unknown) {
   393         if (chipID != 0 && chipID != 0xffff) {
   394           IT87Exit();
   395 
   396           ReportUnknownChip("ITE", chipID);
   397         }
   398       } else {
   399         Select(IT87_ENVIRONMENT_CONTROLLER_LDN);
   400         ushort address = ReadWord(BASE_ADDRESS_REGISTER);
   401         Thread.Sleep(1);
   402         ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
   403 
   404         byte version = (byte)(ReadByte(IT87_CHIP_VERSION_REGISTER) & 0x0F);
   405 
   406         ushort gpioAddress;
   407         ushort gpioVerify;
   408         if (chip == Chip.IT8705F) {
   409           Select(IT8705_GPIO_LDN);
   410           gpioAddress = ReadWord(BASE_ADDRESS_REGISTER);
   411           Thread.Sleep(1);
   412           gpioVerify = ReadWord(BASE_ADDRESS_REGISTER);
   413         } else {
   414           Select(IT87XX_GPIO_LDN);
   415           gpioAddress = ReadWord(BASE_ADDRESS_REGISTER + 2);
   416           Thread.Sleep(1);
   417           gpioVerify = ReadWord(BASE_ADDRESS_REGISTER + 2);
   418         }
   419         
   420         IT87Exit();
   421 
   422         if (address != verify || address < 0x100 || (address & 0xF007) != 0) {
   423           report.Append("Chip ID: 0x");
   424           report.AppendLine(chip.ToString("X"));
   425           report.Append("Error: Invalid address 0x");
   426           report.AppendLine(address.ToString("X",
   427             CultureInfo.InvariantCulture));
   428           report.AppendLine();
   429           return false;
   430         }
   431 
   432         if (gpioAddress != gpioVerify || gpioAddress < 0x100 ||
   433           (gpioAddress & 0xF007) != 0) {
   434           report.Append("Chip ID: 0x");
   435           report.AppendLine(chip.ToString("X"));
   436           report.Append("Error: Invalid GPIO address 0x");
   437           report.AppendLine(gpioAddress.ToString("X",
   438             CultureInfo.InvariantCulture));
   439           report.AppendLine();
   440           return false;
   441         }
   442 
   443         superIOs.Add(new IT87XX(chip, address, gpioAddress, version));
   444         return true;
   445       }
   446 
   447       return false;
   448     }
   449 
   450     #endregion
   451 
   452     #region SMSC
   453 
   454     private void SMSCEnter() {
   455       Ring0.WriteIoPort(registerPort, 0x55);
   456     }
   457 
   458     private void SMSCExit() {
   459       Ring0.WriteIoPort(registerPort, 0xAA);
   460     }
   461 
   462     private bool DetectSMSC() {
   463       SMSCEnter();
   464 
   465       ushort chipID = ReadWord(CHIP_ID_REGISTER);
   466       Chip chip;
   467       switch (chipID) {
   468         default: chip = Chip.Unknown; break;
   469       }
   470       if (chip == Chip.Unknown) {
   471         if (chipID != 0 && chipID != 0xffff) {
   472           SMSCExit();
   473 
   474           ReportUnknownChip("SMSC", chipID);
   475         }
   476       } else {
   477         SMSCExit();
   478         return true;
   479       }
   480 
   481       return false;
   482     }
   483 
   484     #endregion
   485 
   486     private void Detect() {
   487 
   488       for (int i = 0; i < REGISTER_PORTS.Length; i++) {
   489         registerPort = REGISTER_PORTS[i];
   490         valuePort = VALUE_PORTS[i];
   491 
   492         if (DetectWinbondFintek()) continue;
   493 
   494         if (DetectIT87()) continue;
   495 
   496         if (DetectSMSC()) continue;
   497       }
   498     }
   499 
   500     public LPCIO() {
   501       if (!Ring0.IsOpen)
   502         return;
   503 
   504       if (!Ring0.WaitIsaBusMutex(100))
   505         return;
   506 
   507       Detect();
   508 
   509       Ring0.ReleaseIsaBusMutex();
   510     }
   511 
   512     public ISuperIO[] SuperIO {
   513       get {
   514         return superIOs.ToArray();
   515       }
   516     }
   517 
   518     public string GetReport() {
   519       if (report.Length > 0) {
   520         return "LPCIO" + Environment.NewLine + Environment.NewLine + report;
   521       } else
   522         return null;
   523     }
   524   }
   525 }