|Version 2 (modified by khali, 15 months ago)|
How to figure out the voltage labels and scaling factors
Voltages are the most difficult part to configure. While temperature and fan speed values are generally reported correctly by the drivers directly, voltages are not. The reason is that the ADC used internally to monitor the voltage values has a limited range. Typically the ADC in monitoring chips can monitor from 0 to 4.08 V, 3.06 V, or 2.04 V. Other ranges are possible, but there are a two rules which apply in all cases:
- The ADC cannot measure negative voltages directly.
- The ADC cannot monitor positive voltages beyond its range directly.
In both cases, scaling factors have to be applied. In practice this is achieved using external or internal resistors for positive voltages. The negative voltage case is more complex, but I will not describe it here, as negative voltages are almost unused on recent systems and thus almost never monitored.
The scaling resistors can either be internal to the chip (the chip has dedicated pins for all voltages beyond its ADC range) or external (resistors are soldered on the motherboard.) Internal scaling is easy to deal with and the driver will do it for you. External scaling is motherboard specific and cannot be done by the driver. It has to be done in the libsensors configuration file specifically for every motherboard. Unfortunately the vendors rarely provide enough information to write such configuration statements, so some experimental work is needed. This guide will guide you through this.
First of all, we need reference values to know which result we are supposed to get to. In most cases, the reference will be the BIOS, which typically displays all monitored values. So, boot your machine and go to the BIOS, and write down on a sheet of paper the label and value for every voltage being monitored. If you can spot more than one value per item, this is very useful, so write down all values. The reference can also be a piece of software provided by the vendor.
Once you know which voltages are being monitored and their values on your specific systems, go to Linux and run sensors (or even sensors -u which is even more accurate.) We will try to figure out the proper labels and scaling factors. For this exercise, we need to know the ADC range of the chip. Here is the answer for the most popular Super-I/O chips:
- Old Winbond Super-I/O chips (W83627HF, W083627THF, W83637HF, W83687THF, W83697HF, driver = w83627hf): 0 to 4.08 V, 1 bit = 16 mV
- Old ITE Super-I/O chips (IT8705F, IT8712F, IT8716F, IT8718F, IT8716F, driver = it87): 0 to 4.08 V, 1 bit = 16 mV
- Newer ITE Super-I/O chips (IT8720F, IT8721F, IT8728F, driver = it87): 0 to 3.06 V, 1 bit = 12 mV
- Newer Winbond/Nuvoton Super-I/O chips (W83627EHF, W83627DHG, W83627UHG, W83667HG, NCT6775, NCT6776, driver = w83627ehf): 0 to 2.04 V, 1 bit = 8 mV
- All Fintek Super-I/O chips (driver = f71805f or f71882fg): 0 to 2.04 V, 1 bit = 8 mV
For other chips, try to find the answer in the datasheet, driver documentation or source code.
Then we can start comparing the values reported by the BIOS with the values reported by sensors. I will use the Asus E35M1-I Deluxe motherboard as an example. This board uses an ITE IT8771 chip for hardware monitoring. According to the table above, its ADC ranges from 0 to 3.06V with 1 bit = 12 mV.
The BIOS reports the following voltage values:
CPU: 1.368 V 3.3V: 3.33 V 5V: 5.003 V 12V: 12.072 V
The (unconfigured) sensors command reports the following voltage values:
in0: +1.36 V (min = +2.15 V, max = +2.69 V) ALARM in1: +2.23 V (min = +1.24 V, max = +2.56 V) in2: +2.90 V (min = +1.14 V, max = +0.95 V) ALARM +3.3V: +3.34 V (min = +4.75 V, max = +2.28 V) ALARM in4: +2.23 V (min = +1.51 V, max = +3.05 V) in5: +2.93 V (min = +3.01 V, max = +2.93 V) ALARM in6: +2.23 V (min = +1.60 V, max = +1.63 V) ALARM 3VSB: +3.38 V (min = +2.83 V, max = +2.18 V) ALARM Vbat: +3.29 V
Here we are lucky that some of the inputs have internal mapping and scaling so the driver already applied proper scaling and labels to them. In particular the +3.3V line is already OK. 3VSB and Vbat are also OK, so we get them for free even though the BIOS doesn't even list them. This leaves us with 3 voltage values to map and optionally scale: CPU, 5V and 12V.
We'll start with CPU as it is the easiest one. Voltage of modern processors is relatively low, always inside the ADC range so scaling isn't needed. So we are looking for an input with value 1.368. in0 is a very obvious candidate. Note that we are again lucky here because most modern processors can operate at different voltages depending on the frequency, so the value from sensors doesn't necessarily match what the BIOS shows. Frequency scaling is usually not in effect under the BIOS setup screen so you should either temporarily disable it under Linux as well or put heavy load on the CPU while trying to do the input match. On the plus side, note that the CPU voltage (Vcore) is almost always connected to in0 so it's a safe bet if there's nothing contradicting this hypothesis.
For 5V and 12V, things are a little harder. Both values are above the ADC's range (3.06 V) so we aren't looking only for mappings but also for scaling factors. Unfortunately it's not possible to find the mappings if you don't know the scaling factors (comparing the scaled values from the BIOS with the unscaled values from sensors doesn't work), and it would seem impossible to guess the scaling factors if you don't know the mapping. Looks like a dead end to you? Not really. All this means is that we have to be smart.
Let's search the web for snapshots of BIOS pages from other users with the same board. As motherboards tend to get a lot of online reviews, it's usually not too difficult. Then we write down all sample values we can for 5V and 12V, and add them to the ones we already got from our own board. After sorting, we get the following:
5V samples: 4.942 V, 5.003 V, 5.024 V, 5.147 V 12V samples: 11.872 V, 11.922 V, 12.072 V, 12.122 V
Here again we are somewhat lucky, hunt was good and we have 4 sample values for each input. Each of the values above corresponds to an 8-bit register value, which is multiplied by 12 mV and then by the scaling factor for the respective input. As registers can only hold integer values, we know that the values listed above are separated by an integer number of 12 mV * scaling factor steps, and the integer numbers in question will always be small (typically 1, 2 or 3.) From this we should be able to guess the scaling factor.
Let's start with 12V. The differences between the 4 values are as follows:
11.922 - 11.872 = 0.050 V 12.072 - 11.922 = 0.150 V 12.122 - 12.072 = 0.050 V
From the above, it seems clear that 1 LSB of the ADC corresponds to 50 mV after scaling. And we know that it corresponds to 12 mV before scaling. So we can conclude that the scaling factor is 50/12. And we validate this conclusion as:
12.072 V / (50/12) = 2.897 V
which is close enough to in2 (2.90 V). If it had not worked, we would have tried again with the assumption that 2 LSB of the ADC corresponds to 50 mV after scaling. Then 3 LSB, etc until the candidate scaling factor is below 1, which never happens.
And now we do the same for 5V:
5.147 - 5.024 = 0.123 V 5.024 - 5.003 = 0.021 V 5.003 - 4.942 = 0.061 V
It is slightly more difficult this time because there are some rounding errors, but still: it seems that 1 LSB of the ADC corresponds to about 21 mV after scaling. To limit the rounding errors, best is to divide the difference between the greatest and least values by our estimation of the number of ADC steps between them:
(5.147 - 4.942) / (6 + 1 + 3) = 0.0205 V
so our guess is that 1 LSB of the ADC corresponds to 20.5 mV after scaling. And we know that it corresponds to 12 mV before scaling. So we can conclude that the scaling factor is 20.5/12 or 205/120. And we validate this conclusion as:
5.003 V / (205/120) = 2.929 V
which is close enough to in5 (2.93 V).
Finally we can write the partial configuration file which corresponds to our findings:
chip "it8771-*" ### Voltages # in1, in4 and in6 all read +2.23V, no idea why. label in0 "Vcore" ignore in1 label in2 "+12V" ignore in4 label in5 "+5V" ignore in6 compute in2 @ * (50/12), @ / (50/12) compute in5 @ * (205/120), @ / (205/120) set in0_min 0.7 set in0_max 1.5 set in2_min 12 * 0.95 set in2_max 12 * 1.05 set in3_min 3.3 * 0.95 set in3_max 3.3 * 1.05 set in5_min 5 * 0.95 set in5_max 5 * 1.95 set in7_min 3.3 * 0.95 set in7_max 3.3 * 1.05
And now, good luck with your own motherboard!