Disabling Nuvoton NCT6791D because of fan full speed bug on Asus Z97 WS.
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 //SL: disabling because of fan spinning issue
236 //chip = Chip.NCT6791D;
237 //logicalDeviceNumber = WINBOND_NUVOTON_HARDWARE_MONITOR_LDN;
241 if (chip == Chip.Unknown) {
242 if (id != 0 && id != 0xff) {
243 WinbondNuvotonFintekExit();
245 ReportUnknownChip("Winbond / Nuvoton / Fintek",
246 ((id << 8) | revision));
250 Select(logicalDeviceNumber);
251 ushort address = ReadWord(BASE_ADDRESS_REGISTER);
253 ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
255 ushort vendorID = ReadWord(FINTEK_VENDOR_ID_REGISTER);
257 // disable the hardware monitor i/o space lock on NCT6791D chips
259 if (address == verify && chip == Chip.NCT6791D) {
260 byte options = ReadByte(NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK);
262 // if the i/o space lock is enabled
263 if ((options & 0x10) > 0) {
265 // disable the i/o space lock
266 WriteByte(NUVOTON_HARDWARE_MONITOR_IO_SPACE_LOCK,
267 (byte)(options & ~0x10));
272 WinbondNuvotonFintekExit();
274 if (address != verify) {
275 report.Append("Chip ID: 0x");
276 report.AppendLine(chip.ToString("X"));
277 report.Append("Chip revision: 0x");
278 report.AppendLine(revision.ToString("X",
279 CultureInfo.InvariantCulture));
280 report.AppendLine("Error: Address verification failed");
285 // some Fintek chips have address register offset 0x05 added already
286 if ((address & 0x07) == 0x05)
289 if (address < 0x100 || (address & 0xF007) != 0) {
290 report.Append("Chip ID: 0x");
291 report.AppendLine(chip.ToString("X"));
292 report.Append("Chip revision: 0x");
293 report.AppendLine(revision.ToString("X",
294 CultureInfo.InvariantCulture));
295 report.Append("Error: Invalid address 0x");
296 report.AppendLine(address.ToString("X",
297 CultureInfo.InvariantCulture));
304 case Chip.W83627DHGP:
311 superIOs.Add(new W836XX(chip, revision, address));
317 superIOs.Add(new NCT677X(chip, revision, address));
328 if (vendorID != FINTEK_VENDOR_ID) {
329 report.Append("Chip ID: 0x");
330 report.AppendLine(chip.ToString("X"));
331 report.Append("Chip revision: 0x");
332 report.AppendLine(revision.ToString("X",
333 CultureInfo.InvariantCulture));
334 report.Append("Error: Invalid vendor ID 0x");
335 report.AppendLine(vendorID.ToString("X",
336 CultureInfo.InvariantCulture));
340 superIOs.Add(new F718XX(chip, address));
355 private const byte IT87_ENVIRONMENT_CONTROLLER_LDN = 0x04;
356 private const byte IT8705_GPIO_LDN = 0x05;
357 private const byte IT87XX_GPIO_LDN = 0x07;
358 private const byte IT87_CHIP_VERSION_REGISTER = 0x22;
360 private void IT87Enter() {
361 Ring0.WriteIoPort(registerPort, 0x87);
362 Ring0.WriteIoPort(registerPort, 0x01);
363 Ring0.WriteIoPort(registerPort, 0x55);
364 Ring0.WriteIoPort(registerPort, 0x55);
367 private void IT87Exit() {
368 Ring0.WriteIoPort(registerPort, CONFIGURATION_CONTROL_REGISTER);
369 Ring0.WriteIoPort(valuePort, 0x02);
372 private bool DetectIT87() {
374 // IT87XX can enter only on port 0x2E
375 if (registerPort != 0x2E)
380 ushort chipID = ReadWord(CHIP_ID_REGISTER);
383 case 0x8705: chip = Chip.IT8705F; break;
384 case 0x8712: chip = Chip.IT8712F; break;
385 case 0x8716: chip = Chip.IT8716F; break;
386 case 0x8718: chip = Chip.IT8718F; break;
387 case 0x8720: chip = Chip.IT8720F; break;
388 case 0x8721: chip = Chip.IT8721F; break;
389 case 0x8726: chip = Chip.IT8726F; break;
390 case 0x8728: chip = Chip.IT8728F; break;
391 case 0x8771: chip = Chip.IT8771E; break;
392 case 0x8772: chip = Chip.IT8772E; break;
393 default: chip = Chip.Unknown; break;
395 if (chip == Chip.Unknown) {
396 if (chipID != 0 && chipID != 0xffff) {
399 ReportUnknownChip("ITE", chipID);
402 Select(IT87_ENVIRONMENT_CONTROLLER_LDN);
403 ushort address = ReadWord(BASE_ADDRESS_REGISTER);
405 ushort verify = ReadWord(BASE_ADDRESS_REGISTER);
407 byte version = (byte)(ReadByte(IT87_CHIP_VERSION_REGISTER) & 0x0F);
411 if (chip == Chip.IT8705F) {
412 Select(IT8705_GPIO_LDN);
413 gpioAddress = ReadWord(BASE_ADDRESS_REGISTER);
415 gpioVerify = ReadWord(BASE_ADDRESS_REGISTER);
417 Select(IT87XX_GPIO_LDN);
418 gpioAddress = ReadWord(BASE_ADDRESS_REGISTER + 2);
420 gpioVerify = ReadWord(BASE_ADDRESS_REGISTER + 2);
425 if (address != verify || address < 0x100 || (address & 0xF007) != 0) {
426 report.Append("Chip ID: 0x");
427 report.AppendLine(chip.ToString("X"));
428 report.Append("Error: Invalid address 0x");
429 report.AppendLine(address.ToString("X",
430 CultureInfo.InvariantCulture));
435 if (gpioAddress != gpioVerify || gpioAddress < 0x100 ||
436 (gpioAddress & 0xF007) != 0) {
437 report.Append("Chip ID: 0x");
438 report.AppendLine(chip.ToString("X"));
439 report.Append("Error: Invalid GPIO address 0x");
440 report.AppendLine(gpioAddress.ToString("X",
441 CultureInfo.InvariantCulture));
446 superIOs.Add(new IT87XX(chip, address, gpioAddress, version));
457 private void SMSCEnter() {
458 Ring0.WriteIoPort(registerPort, 0x55);
461 private void SMSCExit() {
462 Ring0.WriteIoPort(registerPort, 0xAA);
465 private bool DetectSMSC() {
468 ushort chipID = ReadWord(CHIP_ID_REGISTER);
471 default: chip = Chip.Unknown; break;
473 if (chip == Chip.Unknown) {
474 if (chipID != 0 && chipID != 0xffff) {
477 ReportUnknownChip("SMSC", chipID);
489 private void Detect() {
491 for (int i = 0; i < REGISTER_PORTS.Length; i++) {
492 registerPort = REGISTER_PORTS[i];
493 valuePort = VALUE_PORTS[i];
495 if (DetectWinbondFintek()) continue;
497 if (DetectIT87()) continue;
499 if (DetectSMSC()) continue;
507 if (!Ring0.WaitIsaBusMutex(100))
512 Ring0.ReleaseIsaBusMutex();
515 public ISuperIO[] SuperIO {
517 return superIOs.ToArray();
521 public string GetReport() {
522 if (report.Length > 0) {
523 return "LPCIO" + Environment.NewLine + Environment.NewLine + report;