From c7e292e282714851c6e7ab1541d0b70ae9f32630 Mon Sep 17 00:00:00 2001 From: erpalma Date: Tue, 11 Dec 2018 11:40:23 +0100 Subject: [PATCH] add power profile-based undervolting (fix #79) --- README.md | 2 +- etc/lenovo_fix.conf | 14 +++++++++++++- lenovo_fix.py | 43 ++++++++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index e215d0d..bb71b5c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ I will keep this list updated. I suggest you to use the excellent **[s-tui](https://github.com/amanusk/s-tui)** tool to check and monitor the CPU usage, frequency, power and temperature under load! ### Undervolt -The script now also supports **undervolting** the CPU by configuring voltage offsets for CPU, cache, GPU, System Agent and Analog I/O planes. The script will re-apply undervolt on resume from standby and hibernate by listening to DBus signals. +The script now also supports **undervolting** the CPU by configuring voltage offsets for CPU, cache, GPU, System Agent and Analog I/O planes. The script will re-apply undervolt on resume from standby and hibernate by listening to DBus signals. You can now either use the `UNDERVOLT` key in config to set global values or the `UNDERVOLT.AC` and `UNDERVOLT.BATTERY` keys to selectively set undervolt values for the two power profiles. ### HWP override (EXPERIMENTAL) I have found that under load my CPU was not always hitting max turbo frequency, in particular when using one/two cores only. For instance, when running [prime95](https://www.mersenne.org/download/) (1 core, test #1) my CPU is limited to about 3500 MHz over the theoretical 4000 MHz maximum. The reason is the value for the HWP energy performance [hints](http://manpages.ubuntu.com/manpages/artful/man8/x86_energy_perf_policy.8.html). By default TLP sets this value to `balance_performance` on AC in order to reduce the power consumption/heat in idle. By setting this value to `performance` I was able to reach 3900 MHz in the prime95 single core test, achieving a +400 MHz boost. Since this value forces the CPU to full speed even during idle, a new experimental feature allows to automatically set HWP to performance under load and revert it to balanced when idle. This feature can be enabled (in AC mode *only*) by setting to `True` the `HWP_Mode` parameter in the config file. diff --git a/etc/lenovo_fix.conf b/etc/lenovo_fix.conf index 3d04ba6..a66ce33 100644 --- a/etc/lenovo_fix.conf +++ b/etc/lenovo_fix.conf @@ -40,7 +40,19 @@ HWP_Mode: False # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) cTDP: 0 -[UNDERVOLT] +[UNDERVOLT.BATTERY] +# CPU core voltage offset (mV) +CORE: 0 +# Integrated GPU voltage offset (mV) +GPU: 0 +# CPU cache voltage offset (mV) +CACHE: 0 +# System Agent voltage offset (mV) +UNCORE: 0 +# Analog I/O voltage offset (mV) +ANALOGIO: 0 + +[UNDERVOLT.AC] # CPU core voltage offset (mV) CORE: 0 # Integrated GPU voltage offset (mV) diff --git a/lenovo_fix.py b/lenovo_fix.py index bd4476a..910a08d 100755 --- a/lenovo_fix.py +++ b/lenovo_fix.py @@ -23,11 +23,9 @@ from threading import Event, Thread from time import time DEFAULT_SYSFS_POWER_PATH = '/sys/class/power_supply/AC*/online' - VOLTAGE_PLANES = {'CORE': 0, 'GPU': 1, 'CACHE': 2, 'UNCORE': 3, 'ANALOGIO': 4} - TRIP_TEMP_RANGE = [40, 97] - +UNDERVOLT_KEYS = ('UNDERVOLT', 'UNDERVOLT.AC', 'UNDERVOLT.BATTERY') power = {'source': None, 'method': 'polling'} platform_info_bits = { @@ -245,7 +243,9 @@ def calc_undervolt_mv(msr_value): def undervolt(config): for plane in VOLTAGE_PLANES: - write_offset_mv = config.getfloat('UNDERVOLT', plane, fallback=0.0) + write_offset_mv = config.getfloat( + 'UNDERVOLT.{:s}'.format(power['source']), plane, fallback=config.getfloat('UNDERVOLT', plane, fallback=0.0) + ) write_value = calc_undervolt_msr(plane, write_offset_mv) writemsr(0x150, write_value) if args.debug: @@ -286,16 +286,29 @@ def load_config(): ) ) - for plane in VOLTAGE_PLANES: - value = config.getfloat('UNDERVOLT', plane) - valid_value = min(0, value) - if value != valid_value: - config.set('UNDERVOLT', plane, str(valid_value)) - print( - '[!] Overriding invalid "UNDERVOLT" value in "{:s}" voltage plane: {:.0f} -> {:.0f}'.format( - plane, value, valid_value - ) - ) + # fix any invalid value (ie. < 0) in the undervolt settings + for key in UNDERVOLT_KEYS: + for plane in VOLTAGE_PLANES: + if key in config: + value = config.getfloat(key, plane) + valid_value = min(0, value) + if value != valid_value: + config.set(key, plane, str(valid_value)) + print( + '[!] Overriding invalid "{:s}" value in "{:s}" voltage plane: {:.0f} -> {:.0f}'.format( + key, plane, value, valid_value + ) + ) + + # handle the case where only one of UNDERVOLT.AC, UNDERVOLT.BATTERY keys exists + # by forcing the other key to all zeros (ie. no undervolt) + if any(key in config for key in UNDERVOLT_KEYS[1:]): + for key in UNDERVOLT_KEYS[1:]: + if key not in config: + config.add_section(key) + for plane in VOLTAGE_PLANES: + value = config.getfloat(key, plane, fallback=0.0) + config.set(key, plane, str(value)) return config @@ -595,7 +608,7 @@ def main(): bus = dbus.SystemBus() # add dbus receiver only if undervolt is enabled in config - if any(config.getfloat('UNDERVOLT', plane) != 0 for plane in VOLTAGE_PLANES): + if any(config.getfloat(key, plane, fallback=0) != 0 for plane in VOLTAGE_PLANES for key in UNDERVOLT_KEYS): bus.add_signal_receiver( handle_sleep_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager', 'org.freedesktop.login1' )