Added a minimal control interface to allow manual fan control and implemented the interface for ATI GPUs.
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.Globalization;
42 namespace OpenHardwareMonitor.Hardware.LPC {
43 internal class W836XX : ISuperIO {
45 private readonly ushort address;
46 private readonly byte revision;
48 private readonly Chip chip;
50 private readonly float?[] voltages = new float?[0];
51 private readonly float?[] temperatures = new float?[0];
52 private readonly float?[] fans = new float?[0];
54 private readonly bool[] peciTemperature = new bool[0];
55 private readonly byte[] voltageRegister = new byte[0];
56 private readonly byte[] voltageBank = new byte[0];
57 private readonly 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 readonly byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
74 private readonly byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };
76 private readonly byte[] FAN_TACHO_REG =
77 new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
78 private readonly byte[] FAN_TACHO_BANK =
79 new byte[] { 0, 0, 0, 0, 5 };
80 private readonly byte[] FAN_BIT_REG =
81 new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
82 private readonly byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
83 private readonly byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
84 private readonly byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };
86 private byte ReadByte(byte bank, byte register) {
88 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
90 (ushort)(address + DATA_REGISTER_OFFSET), bank);
92 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
93 return Ring0.ReadIoPort(
94 (ushort)(address + DATA_REGISTER_OFFSET));
97 private void WriteByte(byte bank, byte register, byte value) {
99 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);
101 (ushort)(address + DATA_REGISTER_OFFSET), bank);
103 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);
105 (ushort)(address + DATA_REGISTER_OFFSET), value);
108 public byte? ReadGPIO(int index) {
112 public void WriteGPIO(int index, byte value) { }
114 public W836XX(Chip chip, byte revision, ushort address) {
115 this.address = address;
116 this.revision = revision;
119 if (!IsWinbondVendor())
122 temperatures = new float?[3];
123 peciTemperature = new bool[3];
127 // note temperature sensor registers that read PECI
128 byte flag = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
129 peciTemperature[0] = (flag & 0x04) != 0;
130 peciTemperature[1] = (flag & 0x40) != 0;
131 peciTemperature[2] = false;
134 case Chip.W83627DHGP:
135 // note temperature sensor registers that read PECI
136 byte sel = ReadByte(0, TEMPERATURE_SOURCE_SELECT_REG);
137 peciTemperature[0] = (sel & 0x07) != 0;
138 peciTemperature[1] = (sel & 0x70) != 0;
139 peciTemperature[2] = false;
143 peciTemperature[0] = false;
144 peciTemperature[1] = false;
145 peciTemperature[2] = false;
151 voltages = new float?[10];
152 voltageRegister = new byte[] {
153 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51, 0x52 };
154 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5, 5 };
155 voltageGain = 0.008f;
156 fans = new float?[5];
159 case Chip.W83627DHGP:
162 voltages = new float?[9];
163 voltageRegister = new byte[] {
164 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x50, 0x51 };
165 voltageBank = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5, 5 };
166 voltageGain = 0.008f;
167 fans = new float?[5];
172 voltages = new float?[7];
173 voltageRegister = new byte[] {
174 0x20, 0x21, 0x22, 0x23, 0x24, 0x50, 0x51 };
175 voltageBank = new byte[] { 0, 0, 0, 0, 0, 5, 5 };
176 voltageGain = 0.016f;
177 fans = new float?[3];
182 private bool IsWinbondVendor() {
184 (ushort)((ReadByte(HIGH_BYTE, VENDOR_ID_REGISTER) << 8) |
185 ReadByte(0, VENDOR_ID_REGISTER));
186 return vendorId == WINBOND_VENDOR_ID;
189 private static ulong SetBit(ulong target, int bit, int value) {
190 if ((value & 1) != value)
191 throw new ArgumentException("Value must be one bit only.");
193 if (bit < 0 || bit > 63)
194 throw new ArgumentException("Bit out of range.");
196 ulong mask = (((ulong)1) << bit);
197 return value > 0 ? target | mask : target & ~mask;
200 public Chip Chip { get { return chip; } }
201 public float?[] Voltages { get { return voltages; } }
202 public float?[] Temperatures { get { return temperatures; } }
203 public float?[] Fans { get { return fans; } }
205 public void Update() {
206 if (!Ring0.WaitIsaBusMutex(10))
209 for (int i = 0; i < voltages.Length; i++) {
210 if (voltageRegister[i] != VOLTAGE_VBAT_REG) {
211 // two special VCore measurement modes for W83627THF
213 if ((chip == Chip.W83627HF || chip == Chip.W83627THF ||
214 chip == Chip.W83687THF) && i == 0)
216 byte vrmConfiguration = ReadByte(0, 0x18);
217 int value = ReadByte(voltageBank[i], voltageRegister[i]);
218 if ((vrmConfiguration & 0x01) == 0)
219 fvalue = 0.016f * value; // VRM8 formula
221 fvalue = 0.00488f * value + 0.69f; // VRM9 formula
223 int value = ReadByte(voltageBank[i], voltageRegister[i]);
224 fvalue = voltageGain * value;
227 voltages[i] = fvalue;
232 bool valid = (ReadByte(0, 0x5D) & 0x01) > 0;
234 voltages[i] = voltageGain * ReadByte(5, VOLTAGE_VBAT_REG);
241 for (int i = 0; i < temperatures.Length; i++) {
242 int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i],
243 TEMPERATURE_REG[i])) << 1;
244 if (TEMPERATURE_BANK[i] > 0)
245 value |= ReadByte(TEMPERATURE_BANK[i],
246 (byte)(TEMPERATURE_REG[i] + 1)) >> 7;
248 float temperature = value / 2.0f;
249 if (temperature <= 125 && temperature >= -55 && !peciTemperature[i]) {
250 temperatures[i] = temperature;
252 temperatures[i] = null;
257 for (int i = 0; i < FAN_BIT_REG.Length; i++)
258 bits = (bits << 8) | ReadByte(0, FAN_BIT_REG[i]);
259 ulong newBits = bits;
260 for (int i = 0; i < fans.Length; i++) {
261 int count = ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]);
263 // assemble fan divisor
264 int divisorBits = (int)(
265 (((bits >> FAN_DIV_BIT2[i]) & 1) << 2) |
266 (((bits >> FAN_DIV_BIT1[i]) & 1) << 1) |
267 ((bits >> FAN_DIV_BIT0[i]) & 1));
268 int divisor = 1 << divisorBits;
270 float value = (count < 0xff) ? 1.35e6f / (count * divisor) : 0;
273 // update fan divisor
274 if (count > 192 && divisorBits < 7)
276 if (count < 96 && divisorBits > 0)
279 newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
280 newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
281 newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);
284 // write new fan divisors
285 for (int i = FAN_BIT_REG.Length - 1; i >= 0; i--) {
286 byte oldByte = (byte)(bits & 0xFF);
287 byte newByte = (byte)(newBits & 0xFF);
289 newBits = newBits >> 8;
290 if (oldByte != newByte)
291 WriteByte(0, FAN_BIT_REG[i], newByte);
294 Ring0.ReleaseIsaBusMutex();
297 public string GetReport() {
298 StringBuilder r = new StringBuilder();
300 r.AppendLine("LPC " + this.GetType().Name);
302 r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
303 r.Append("Chip revision: 0x");
304 r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
305 r.Append("Base Adress: 0x");
306 r.AppendLine(address.ToString("X4", CultureInfo.InvariantCulture));
309 if (!Ring0.WaitIsaBusMutex(100))
312 r.AppendLine("Hardware Monitor Registers");
314 r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
316 for (int i = 0; i <= 0x7; i++) {
318 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
320 for (int j = 0; j <= 0xF; j++) {
322 r.Append(ReadByte(0, (byte)((i << 4) | j)).ToString(
323 "X2", CultureInfo.InvariantCulture));
327 for (int k = 1; k <= 15; k++) {
328 r.AppendLine("Bank " + k);
329 for (int i = 0x5; i < 0x6; i++) {
331 r.Append((i << 4).ToString("X2", CultureInfo.InvariantCulture));
333 for (int j = 0; j <= 0xF; j++) {
335 r.Append(ReadByte((byte)(k), (byte)((i << 4) | j)).ToString(
336 "X2", CultureInfo.InvariantCulture));
343 Ring0.ReleaseIsaBusMutex();