Browse Source

add more configuration options and battery/ac profiles

erpalma 7 years ago
parent
commit
d949863e60
1 changed files with 73 additions and 8 deletions
  1. 73 8
      lenovo_fix.py

+ 73 - 8
lenovo_fix.py

@@ -7,7 +7,24 @@ import struct
 from periphery import MMIO
 from time import sleep
 
-UPDATE_RATE_SEC = 15
+config = {
+    'AC': {
+        'UPDATE_RATE_SEC': 5,  # Update the registers every this many seconds
+        'PL1_TDP_W': 44,  # Max package power for time window #1
+        'PL1_DURATION_S': 28,  # Time window #1 duration
+        'PL2_TDP_W': 44,  # Max package power for time window #1
+        'PL2_DURATION_S': 0.002,  # Time window #1 duration
+        'TRIP_TEMP_C': 97  # Max allowed temperature before throttling
+    },
+    'BATTERY': {
+        'UPDATE_RATE_SEC': 30,  # Update the registers every this many seconds
+        'PL1_TDP_W': 29,  # Max package power for time window #1
+        'PL1_DURATION_S': 28,  # Time window #1 duration
+        'PL2_TDP_W': 44,  # Max package power for time window #1
+        'PL2_DURATION_S': 0.002,  # Time window #1 duration
+        'TRIP_TEMP_C': 85  # Max allowed temperature before throttling
+    },
+}
 
 
 def writemsr(msr, val):
@@ -21,17 +38,65 @@ def writemsr(msr, val):
         raise OSError("msr module not loaded (run modprobe msr)")
 
 
+def is_on_battery():
+    with open('/sys/class/power_supply/AC/online') as f:
+        return not bool(int(f.read()))
+
+
+def calc_time_window_vars(t):
+    for Y in xrange(2**5):
+        for Z in xrange(2**2):
+            if t <= (2**Y) * (1. + Z / 4.) * 0.000977:
+                return (Y, Z)
+    raise Exception('Unable to find a good combination!')
+
+
+def check_config():
+    for k in config:
+        assert 0 < config[k]['UPDATE_RATE_SEC']
+        assert 0 < config[k]['PL1_TDP_W']
+        assert 0 < config[k]['PL2_TDP_W']
+        assert 0 < config[k]['PL1_DURATION_S']
+        assert 0 < config[k]['PL2_DURATION_S']
+        assert 40 < config[k]['TRIP_TEMP_C'] < 98
+
+
+def calc_reg_values():
+    for k in config:
+        # the critical temperature for this CPU is 100 C
+        trip_offset = int(round(100 - config[k]['TRIP_TEMP_C']))
+        config[k]['MSR_TEMPERATURE_TARGET'] = trip_offset << 24
+
+        # 0.125 is the power unit of this CPU
+        PL1 = int(round(config[k]['PL1_TDP_W'] / 0.125))
+        Y, Z = calc_time_window_vars(config[k]['PL1_DURATION_S'])
+        TW1 = Y | (Z << 5)
+
+        PL2 = int(round(config[k]['PL2_TDP_W'] / 0.125))
+        Y, Z = calc_time_window_vars(config[k]['PL2_DURATION_S'])
+        TW2 = Y | (Z << 5)
+
+        config[k]['MSR_PKG_POWER_LIMIT'] = PL1 | (1 << 15) | (TW1 << 17) | (PL2 << 32) | (1 << 47) | (TW2 << 49)
+
+
 def main():
+    check_config()
+    calc_reg_values()
+
     mchbar_mmio = MMIO(0xfed159a0, 8)
     while True:
-        # set temperature trip point to 97 C
-        writemsr(0x1a2, 0x3000000)
-        # set MSR to PL1 45W, max duration - PL2 45W, 2ms
-        writemsr(0x610, 0x42816800fe8168)
+        cur_config = config['BATTERY' if is_on_battery() else 'AC']
+
+        # set temperature trip point
+        writemsr(0x1a2, cur_config['MSR_TEMPERATURE_TARGET'])
+
+        # set PL1/2 on MSR
+        writemsr(0x610, cur_config['MSR_PKG_POWER_LIMIT'])
         # set MCHBAR register to the same PL1/2 values
-        mchbar_mmio.write32(0, 0x00fe8168)
-        mchbar_mmio.write32(4, 0x00428168)
-        sleep(UPDATE_RATE_SEC)
+        mchbar_mmio.write32(0, cur_config['MSR_PKG_POWER_LIMIT'] & 0xffffffff)
+        mchbar_mmio.write32(4, cur_config['MSR_PKG_POWER_LIMIT'] >> 32)
+
+        sleep(cur_config['UPDATE_RATE_SEC'])
 
 
 if __name__ == '__main__':