add undervolt capabilities
This commit is contained in:
@@ -30,3 +30,15 @@ PL2_Tdp_W: 44
|
|||||||
PL2_Duration_S: 0.002
|
PL2_Duration_S: 0.002
|
||||||
# Max allowed temperature before throttling
|
# Max allowed temperature before throttling
|
||||||
Trip_Temp_C: 97
|
Trip_Temp_C: 97
|
||||||
|
|
||||||
|
[UNDERVOLT]
|
||||||
|
# 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
|
||||||
|
|||||||
@@ -1,18 +1,33 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python2
|
||||||
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
import dbus
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from dbus.mainloop.glib import DBusGMainLoop
|
||||||
from periphery import MMIO
|
from periphery import MMIO
|
||||||
from time import sleep
|
from threading import Event, Thread
|
||||||
|
|
||||||
|
try:
|
||||||
|
from gi.repository import GObject
|
||||||
|
except ImportError:
|
||||||
|
import gobject as GObject
|
||||||
|
|
||||||
SYSFS_POWER_PATH = '/sys/class/power_supply/AC/online'
|
SYSFS_POWER_PATH = '/sys/class/power_supply/AC/online'
|
||||||
CONFIG_PATH = '/etc/lenovo_fix.conf'
|
CONFIG_PATH = '/etc/lenovo_fix.conf'
|
||||||
|
|
||||||
|
VOLTAGE_PLANES = {
|
||||||
|
'CORE': 0,
|
||||||
|
'GPU': 1,
|
||||||
|
'CACHE': 2,
|
||||||
|
'UNCORE': 3,
|
||||||
|
'ANALOGIO': 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def writemsr(msr, val):
|
def writemsr(msr, val):
|
||||||
n = glob.glob('/dev/cpu/[0-9]*/msr')
|
n = glob.glob('/dev/cpu/[0-9]*/msr')
|
||||||
@@ -41,6 +56,19 @@ def calc_time_window_vars(t):
|
|||||||
raise Exception('Unable to find a good combination!')
|
raise Exception('Unable to find a good combination!')
|
||||||
|
|
||||||
|
|
||||||
|
def undervolt(config):
|
||||||
|
for plane in VOLTAGE_PLANES:
|
||||||
|
writemsr(0x150, calc_undervolt_msr(plane, config.getfloat('UNDERVOLT', plane)))
|
||||||
|
|
||||||
|
|
||||||
|
def calc_undervolt_msr(plane, offset):
|
||||||
|
assert offset <= 0
|
||||||
|
assert plane in VOLTAGE_PLANES
|
||||||
|
offset = int(round(offset * 1.024))
|
||||||
|
offset = 0xFFE00000 & ((offset & 0xFFF) << 21)
|
||||||
|
return 0x8000001100000000 | (VOLTAGE_PLANES[plane] << 40) | offset
|
||||||
|
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
config = ConfigParser.ConfigParser()
|
config = ConfigParser.ConfigParser()
|
||||||
config.read(CONFIG_PATH)
|
config.read(CONFIG_PATH)
|
||||||
@@ -53,6 +81,9 @@ def load_config():
|
|||||||
assert 0 < config.getfloat(power_source, 'PL2_Duration_S')
|
assert 0 < config.getfloat(power_source, 'PL2_Duration_S')
|
||||||
assert 40 < config.getfloat(power_source, 'Trip_Temp_C') < 98
|
assert 40 < config.getfloat(power_source, 'Trip_Temp_C') < 98
|
||||||
|
|
||||||
|
for plane in VOLTAGE_PLANES:
|
||||||
|
assert config.getfloat('UNDERVOLT', plane) <= 0
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@@ -78,15 +109,10 @@ def calc_reg_values(config):
|
|||||||
return regs
|
return regs
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def power_thread(config, regs, exit_event):
|
||||||
config = load_config()
|
|
||||||
regs = calc_reg_values(config)
|
|
||||||
|
|
||||||
if not config.getboolean('GENERAL', 'Enabled'):
|
|
||||||
return
|
|
||||||
|
|
||||||
mchbar_mmio = MMIO(0xfed159a0, 8)
|
mchbar_mmio = MMIO(0xfed159a0, 8)
|
||||||
while True:
|
|
||||||
|
while not exit_event.is_set():
|
||||||
power_source = 'BATTERY' if is_on_battery() else 'AC'
|
power_source = 'BATTERY' if is_on_battery() else 'AC'
|
||||||
|
|
||||||
# set temperature trip point
|
# set temperature trip point
|
||||||
@@ -98,7 +124,45 @@ def main():
|
|||||||
mchbar_mmio.write32(0, regs[power_source]['MSR_PKG_POWER_LIMIT'] & 0xffffffff)
|
mchbar_mmio.write32(0, regs[power_source]['MSR_PKG_POWER_LIMIT'] & 0xffffffff)
|
||||||
mchbar_mmio.write32(4, regs[power_source]['MSR_PKG_POWER_LIMIT'] >> 32)
|
mchbar_mmio.write32(4, regs[power_source]['MSR_PKG_POWER_LIMIT'] >> 32)
|
||||||
|
|
||||||
sleep(config.getfloat(power_source, 'Update_Rate_s'))
|
exit_event.wait(config.getfloat(power_source, 'Update_Rate_s'))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config = load_config()
|
||||||
|
regs = calc_reg_values(config)
|
||||||
|
|
||||||
|
if not config.getboolean('GENERAL', 'Enabled'):
|
||||||
|
return
|
||||||
|
|
||||||
|
exit_event = Event()
|
||||||
|
t = Thread(target=power_thread, args=(config, regs, exit_event))
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
undervolt(config)
|
||||||
|
|
||||||
|
# handle dbus events for applying undervolt on resume from sleep/hybernate
|
||||||
|
def handle_sleep_callback(sleeping):
|
||||||
|
if not sleeping:
|
||||||
|
undervolt(config)
|
||||||
|
|
||||||
|
DBusGMainLoop(set_as_default=True)
|
||||||
|
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):
|
||||||
|
bus.add_signal_receiver(handle_sleep_callback, 'PrepareForSleep', 'org.freedesktop.login1.Manager',
|
||||||
|
'org.freedesktop.login1')
|
||||||
|
|
||||||
|
try:
|
||||||
|
GObject.threads_init()
|
||||||
|
loop = GObject.MainLoop()
|
||||||
|
loop.run()
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
pass
|
||||||
|
|
||||||
|
exit_event.set()
|
||||||
|
loop.quit()
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in New Issue
Block a user