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