Fixed Issue 651.
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/.
7 Copyright (C) 2009-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
13 using System.Globalization;
15 using System.Threading;
17 namespace OpenHardwareMonitor.Hardware.LPC {
18 internal class LPCIO {
20 private readonly List<ISuperIO> superIOs = new List<ISuperIO>();
21 private readonly StringBuilder report = new StringBuilder();
24 private readonly ushort[] REGISTER_PORTS = new ushort[] { 0x2E, 0x4E };
25 private readonly ushort[] VALUE_PORTS = new ushort[] { 0x2F, 0x4F };
27 private ushort registerPort;
28 private ushort valuePort;
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;
37 private byte ReadByte(byte register) {
38 Ring0.WriteIoPort(registerPort, register);
39 return Ring0.ReadIoPort(valuePort);
42 private void WriteByte(byte register, byte value) {
43 Ring0.WriteIoPort(registerPort, register);
44 Ring0.WriteIoPort(valuePort, value);
47 private ushort ReadWord(byte register) {
48 return (ushort)((ReadByte(register) << 8) |
49 ReadByte((byte)(register + 1)));
52 private void Select(byte logicalDeviceNumber) {
53 Ring0.WriteIoPort(registerPort, DEVCIE_SELECT_REGISTER);
54 Ring0.WriteIoPort(valuePort, logicalDeviceNumber);
57 private void ReportUnknownChip(string type, int chip) {
58 report.Append("Chip ID: Unknown ");
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));
65 report.AppendLine(valuePort.ToString("X", CultureInfo.InvariantCulture));
69 #region Winbond, Nuvoton, Fintek
71 private const byte FINTEK_VENDOR_ID_REGISTER = 0x23;
72 private const ushort FINTEK_VENDOR_ID = 0x1934;
74 private const byte WINBOND_NUVOTON_HARDWARE_MONITOR_LDN = 0x0B;
76 private const byte F71858_HARDWARE_MONITOR_LDN = 0x02;
77 private const byte FINTEK_HARDWARE_MONITOR_LDN = 0x04;
79 private const byte NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK = 0x28;
81 private void WinbondNuvotonFintekEnter() {
82 Ring0.WriteIoPort(registerPort, 0x87);
83 Ring0.WriteIoPort(registerPort, 0x87);
86 private void WinbondNuvotonFintekExit() {
87 Ring0.WriteIoPort(registerPort, 0xAA);
90 private bool DetectWinbondFintek() {
91 WinbondNuvotonFintekEnter();
93 byte logicalDeviceNumber = 0;
94 byte id = ReadByte(CHIP_ID_REGISTER);
95 byte revision = ReadByte(CHIP_REVISION_REGISTER);
96 Chip chip = Chip.Unknown;
102 logicalDeviceNumber = F71858_HARDWARE_MONITOR_LDN;
106 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
113 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
120 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
127 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
134 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
137 chip = Chip.F71889ED;
138 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
144 chip = Chip.F71889AD;
145 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
149 logicalDeviceNumber = FINTEK_HARDWARE_MONITOR_LDN;
157 chip = Chip.W83627HF;
158 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
162 switch (revision & 0xF0) {
164 chip = Chip.W83627THF;
165 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
171 chip = Chip.W83687THF;
172 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
176 switch (revision & 0xF0) {
179 chip = Chip.W83627EHF;
180 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
184 switch (revision & 0xF0) {
186 chip = Chip.W83627DHG;
187 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
191 switch (revision & 0xF0) {
193 chip = Chip.W83667HG;
194 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
198 switch (revision & 0xF0) {
200 chip = Chip.W83627DHGP;
201 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
205 switch (revision & 0xF0) {
207 chip = Chip.W83667HGB;
208 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
212 switch (revision & 0xF0) {
214 chip = Chip.NCT6771F;
215 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
219 switch (revision & 0xF0) {
221 chip = Chip.NCT6776F;
222 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
226 switch (revision & 0xF0) {
228 chip = Chip.NCT6779D;
229 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
235 chip = Chip.NCT6791D;
236 logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
240 if (chip == Chip.Unknown) {
241 if (id != 0 && id != 0xff) {
242 WinbondNuvotonFintekExit();
244 ReportUnknownChip("Winbond / Nuvoton / Fintek",
245 ((id << 8) | revision));
249 Select(logicalDeviceNumber);
250 ushort address = ReadWord(BASE_ADDRESS_REGISTER);
252 ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
254 ushort vendorID = ReadWord(FINTEK_VENDOR_ID_REGISTER);
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);
260 // if the i/o space lock is enabled
261 if ((options & 0x10) > 0) {
263 // disable the i/o space lock
264 WriteByte(NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK,
265 (byte)(options & ~0x10));
269 WinbondNuvotonFintekExit();
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");
282 // some Fintek chips have address register offset 0x05 added already
283 if ((address & 0x07) == 0x05)
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));
301 case Chip.W83627DHGP:
308 superIOs.Add(new W836XX(chip, revision, address));
314 superIOs.Add(new NCT677X(chip, revision, address));
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));
337 superIOs.Add(new F718XX(chip, address));
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;
357 private void IT87Enter() {
358 Ring0.WriteIoPort(registerPort, 0x87);
359 Ring0.WriteIoPort(registerPort, 0x01);
360 Ring0.WriteIoPort(registerPort, 0x55);
361 Ring0.WriteIoPort(registerPort, 0x55);
364 private void IT87Exit() {
365 Ring0.WriteIoPort(registerPort, CONFIGURATION_CONTROL_REGISTER);
366 Ring0.WriteIoPort(valuePort, 0x02);
369 private bool DetectIT87() {
371 // IT87XX can enter only on port 0x2E
372 if (registerPort != 0x2E)
377 ushort chipID = ReadWord(CHIP_ID_REGISTER);
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;
392 if (chip == Chip.Unknown) {
393 if (chipID != 0 && chipID != 0xffff) {
396 ReportUnknownChip("ITE", chipID);
399 Select(IT87_ENVIRONMENT_CONTROLLER_LDN);
400 ushort address = ReadWord(BASE_ADDRESS_REGISTER);
402 ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
404 byte version = (byte)(ReadByte(IT87_CHIP_VERSION_REGISTER) & 0x0F);
408 if (chip == Chip.IT8705F) {
409 Select(IT8705_GPIO_LDN);
410 gpioAddress = ReadWord(BASE_ADDRESS_REGISTER);
412 gpioVerify = ReadWord(BASE_ADDRESS_REGISTER);
414 Select(IT87XX_GPIO_LDN);
415 gpioAddress = ReadWord(BASE_ADDRESS_REGISTER + 2);
417 gpioVerify = ReadWord(BASE_ADDRESS_REGISTER + 2);
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));
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));
443 superIOs.Add(new IT87XX(chip, address, gpioAddress, version));
454 private void SMSCEnter() {
455 Ring0.WriteIoPort(registerPort, 0x55);
458 private void SMSCExit() {
459 Ring0.WriteIoPort(registerPort, 0xAA);
462 private bool DetectSMSC() {
465 ushort chipID = ReadWord(CHIP_ID_REGISTER);
468 default: chip = Chip.Unknown; break;
470 if (chip == Chip.Unknown) {
471 if (chipID != 0 && chipID != 0xffff) {
474 ReportUnknownChip("SMSC", chipID);
486 private void Detect() {
488 for (int i = 0; i < REGISTER_PORTS.Length; i++) {
489 registerPort = REGISTER_PORTS[i];
490 valuePort = VALUE_PORTS[i];
492 if (DetectWinbondFintek()) continue;
494 if (DetectIT87()) continue;
496 if (DetectSMSC()) continue;
504 if (!Ring0.WaitIsaBusMutex(100))
509 Ring0.ReleaseIsaBusMutex();
512 public ISuperIO[] SuperIO {
514 return superIOs.ToArray();
518 public string GetReport() {
519 if (report.Length > 0) {
520 return "LPCIO" + Environment.NewLine + Environment.NewLine + report;