Added experimental lm-sensors super I/O support for Linux.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2009-2010
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Collections.Generic;
42 namespace OpenHardwareMonitor.Hardware.LPC {
43 public class W836XX : ISuperIO {
45 private ushort address;
46 private byte revision;
50 private float?[] voltages = new float?[0];
51 private float?[] temperatures = new float?[0];
52 private float?[] fans = new float?[0];
54 private bool[] peciTemperature = new bool[0];
55 private byte[] voltageRegister = new byte[0];
56 private byte[] voltageBank = new byte[0];
57 private float voltageGain = 0.008f;
60 private const ushort WINBOND_VENDOR_ID = 0x5CA3;
61 private const byte HIGH_BYTE = 0x80;
64 private const byte ADDRESS_REGISTER_OFFSET = 0x05;
65 private const byte DATA_REGISTER_OFFSET = 0x06;
67 // Hardware Monitor Registers
68 private const byte VOLTAGE_VBAT_REG = 0x51;
69 private const byte BANK_SELECT_REGISTER = 0x4E;
70 private const byte VENDOR_ID_REGISTER = 0x4F;
71 private const byte TEMPERATURE_SOURCE_SELECT_REG = 0x49;
73 private byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
74 private byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };
76 private byte[] FAN_TACHO_REG = new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
77 private byte[] FAN_TACHO_BANK = new byte[] { 0, 0, 0, 0, 5 };
78 private byte[] FAN_BIT_REG = new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
79 private byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
80 private byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
81 private byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };
83 private byte ReadByte(byte bank, byte register) {
84 WinRing0.WriteIoPortByte(
85 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
86 WinRing0.WriteIoPortByte(
87 (ushort)(address + DATA_REGISTER_OFFSET), bank);
88 WinRing0.WriteIoPortByte(
89 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
90 return WinRing0.ReadIoPortByte(
91 (ushort)(address + DATA_REGISTER_OFFSET));
94 private void WriteByte(byte bank, byte register, byte value) {
95 WinRing0.WriteIoPortByte(
96 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
97 WinRing0.WriteIoPortByte(
98 (ushort)(address + DATA_REGISTER_OFFSET), bank);
99 WinRing0.WriteIoPortByte(
100 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
101 WinRing0.WriteIoPortByte(
102 (ushort)(address + DATA_REGISTER_OFFSET), value);
105 public W836XX(Chip chip, byte revision, ushort address) {
106 this.address = address;
107 this.revision = revision;
110 if (!IsWinbondVendor())
113 temperatures = new float?[3];
114 peciTemperature = new bool[3];
118 // note temperature sensor registers that read PECI
119 byte flag = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
120 peciTemperature[0] = (flag & 0x04) != 0;
121 peciTemperature[1] = (flag & 0x40) != 0;
122 peciTemperature[2] = false;
125 case Chip.W83627DHGP:
126 // do not add temperature sensor registers that read PECI
127 byte sel = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
128 peciTemperature[0] = (sel & 0x07) != 0;
129 peciTemperature[1] = (sel & 0x70) != 0;
130 peciTemperature[2] = false;
134 peciTemperature[0] = false;
135 peciTemperature[1] = false;
136 peciTemperature[2] = false;
142 voltages = new float?[10];
143 voltageRegister = new byte[] {
144 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51, 0x52 };
145 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5, 5 };
146 voltageGain = 0.008f;
147 fans = new float?[5];
150 case Chip.W83627DHGP:
153 voltages = new float?[9];
154 voltageRegister = new byte[] {
155 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51 };
156 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5 };
157 voltageGain = 0.008f;
158 fans = new float?[5];
163 voltages = new float?[7];
164 voltageRegister = new byte[] {
165 0x20, 0x21, 0x22, 0x23, 0x24, 0x50, 0x51 };
166 voltageBank = new byte[] { 0, 0, 0, 0, 0, 5, 5 };
167 voltageGain = 0.016f;
168 fans = new float?[3];
173 private bool IsWinbondVendor() {
175 (ushort)((ReadByte(HIGH_BYTE, VENDOR_ID_REGISTER) << 8) |
176 ReadByte(0, VENDOR_ID_REGISTER));
177 return vendorId == WINBOND_VENDOR_ID;
180 private ulong SetBit(ulong target, int bit, int value) {
181 if ((value & 1) != value)
182 throw new ArgumentException("Value must be one bit only.");
184 if (bit < 0 || bit > 63)
185 throw new ArgumentException("Bit out of range.");
187 ulong mask = (((ulong)1) << bit);
188 return value > 0 ? target | mask : target & ~mask;
191 public Chip Chip { get { return chip; } }
192 public float?[] Voltages { get { return voltages; } }
193 public float?[] Temperatures { get { return temperatures; } }
194 public float?[] Fans { get { return fans; } }
196 public void Update() {
198 for (int i = 0; i < voltages.Length; i++) {
199 if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
200 // two special VCore measurement modes for W83627THF
202 if ((chip == Chip.W83627HF || chip == Chip.W83627THF ||
203 chip == Chip.W83687THF) && i == 0)
205 byte vrmConfiguration = ReadByte(0, 0x18);
206 int value = ReadByte(voltageBank[i], voltageRegister[i]);
207 if ((vrmConfiguration & 0x01) == 0)
208 fvalue = 0.016f * value; // VRM8 formula
210 fvalue = 0.00488f * value + 0.69f; // VRM9 formula
212 int value = ReadByte(voltageBank[i], voltageRegister[i]);
213 fvalue = voltageGain * value;
216 voltages[i] = fvalue;
221 bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
223 voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
230 for (int i = 0; i < temperatures.Length; i++) {
231 int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i],
232 TEMPERATURE_REG[i])) << 1;
233 if (TEMPERATURE_BANK[i] > 0)
234 value |= ReadByte(TEMPERATURE_BANK[i],
235 (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
237 float temperature = value / 2.0f;
238 if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
239 temperatures[i] = temperature;
241 temperatures[i] = null;
246 for (int i = 0; i < FAN_BIT_REG.Length; i++)
247 bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
248 ulong newBits = bits;
249 for (int i = 0; i < fans.Length; i++) {
250 int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
252 // assemble fan divisor
253 int divisorBits = (int)(
254 (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
255 (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
256 ((bits >> FAN_DIV_BIT0[i]) & 1));
257 int divisor = 1 << divisorBits;
259 float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
262 // update fan divisor
263 if (count > 192 && divisorBits < 7)
265 if (count < 96 && divisorBits > 0)
268 newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
269 newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
270 newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
273 // write new fan divisors
274 for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
275 byte oldByte = (byte)(bits & 0xFF);
276 byte newByte = (byte)(newBits & 0xFF);
278 newBits = newBits >> 8;
279 if (oldByte != newByte)
280 WriteByte(0, FAN_BIT_REG[i], newByte);
284 public string GetReport() {
285 StringBuilder r = new StringBuilder();
287 r.AppendLine("LPC " + this.GetType().Name);
289 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
290 r.Append("Chip revision: 0x"); r.AppendLine(revision.ToString("X"));
291 r.Append("Base Adress: 0x"); r.AppendLine(address.ToString("X4"));
293 r.AppendLine("Hardware Monitor Registers");
295 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
297 for (int i = 0; i <= 0x7; i++) {
298 r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append(" ");
299 for (int j = 0; j <= 0xF; j++) {
301 r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString("X2"));
305 for (int k = 1; k <= 15; k++) {
306 r.AppendLine("Bank " + k);
307 for (int i = 0x5; i < 0x6; i++) {
308 r.Append(" "); r.Append((i << 4).ToString("X2")); r.Append(" ");
309 for (int j = 0; j <= 0xF; j++) {
311 r.Append(ReadByte((byte)(k),
312 (byte)((i << 4) | j)).ToString("X2"));