nixos/preload: init
[NixPkgs.git] / nixos / modules / services / hardware / undervolt.nix
blob258f09bbab09fe5c0830f956143b8486eda39bdb
1 { config, pkgs, lib, ... }:
3 with lib;
4 let
5   cfg = config.services.undervolt;
7   mkPLimit = limit: window:
8     if (limit == null && window == null) then null
9     else assert asserts.assertMsg (limit != null && window != null) "Both power limit and window must be set";
10       "${toString limit} ${toString window}";
11   cliArgs = lib.cli.toGNUCommandLine {} {
12     inherit (cfg)
13       verbose
14       temp
15       ;
16     # `core` and `cache` are both intentionally set to `cfg.coreOffset` as according to the undervolt docs:
17     #
18     #     Core or Cache offsets have no effect. It is not possible to set different offsets for
19     #     CPU Core and Cache. The CPU will take the smaller of the two offsets, and apply that to
20     #     both CPU and Cache. A warning message will be displayed if you attempt to set different offsets.
21     core = cfg.coreOffset;
22     cache = cfg.coreOffset;
23     gpu = cfg.gpuOffset;
24     uncore = cfg.uncoreOffset;
25     analogio = cfg.analogioOffset;
27     temp-bat = cfg.tempBat;
28     temp-ac = cfg.tempAc;
30     power-limit-long = mkPLimit cfg.p1.limit cfg.p1.window;
31     power-limit-short = mkPLimit cfg.p2.limit cfg.p2.window;
32   };
35   options.services.undervolt = {
36     enable = mkEnableOption (lib.mdDoc ''
37        Undervolting service for Intel CPUs.
39        Warning: This service is not endorsed by Intel and may permanently damage your hardware. Use at your own risk!
40     '');
42     verbose = mkOption {
43       type = types.bool;
44       default = false;
45       description = lib.mdDoc ''
46         Whether to enable verbose logging.
47       '';
48     };
50     package = mkOption {
51       type = types.package;
52       default = pkgs.undervolt;
53       defaultText = literalExpression "pkgs.undervolt";
54       description = lib.mdDoc ''
55         undervolt derivation to use.
56       '';
57     };
59     coreOffset = mkOption {
60       type = types.nullOr types.int;
61       default = null;
62       description = lib.mdDoc ''
63         The amount of voltage in mV to offset the CPU cores by.
64       '';
65     };
67     gpuOffset = mkOption {
68       type = types.nullOr types.int;
69       default = null;
70       description = lib.mdDoc ''
71         The amount of voltage in mV to offset the GPU by.
72       '';
73     };
75     uncoreOffset = mkOption {
76       type = types.nullOr types.int;
77       default = null;
78       description = lib.mdDoc ''
79         The amount of voltage in mV to offset uncore by.
80       '';
81     };
83     analogioOffset = mkOption {
84       type = types.nullOr types.int;
85       default = null;
86       description = lib.mdDoc ''
87         The amount of voltage in mV to offset analogio by.
88       '';
89     };
91     temp = mkOption {
92       type = types.nullOr types.int;
93       default = null;
94       description = lib.mdDoc ''
95         The temperature target in Celsius degrees.
96       '';
97     };
99     tempAc = mkOption {
100       type = types.nullOr types.int;
101       default = null;
102       description = lib.mdDoc ''
103         The temperature target on AC power in Celsius degrees.
104       '';
105     };
107     tempBat = mkOption {
108       type = types.nullOr types.int;
109       default = null;
110       description = lib.mdDoc ''
111         The temperature target on battery power in Celsius degrees.
112       '';
113     };
115     p1.limit = mkOption {
116       type = with types; nullOr int;
117       default = null;
118       description = lib.mdDoc ''
119         The P1 Power Limit in Watts.
120         Both limit and window must be set.
121       '';
122     };
123     p1.window = mkOption {
124       type = with types; nullOr (oneOf [ float int ]);
125       default = null;
126       description = lib.mdDoc ''
127         The P1 Time Window in seconds.
128         Both limit and window must be set.
129       '';
130     };
132     p2.limit = mkOption {
133       type = with types; nullOr int;
134       default = null;
135       description = lib.mdDoc ''
136         The P2 Power Limit in Watts.
137         Both limit and window must be set.
138       '';
139     };
140     p2.window = mkOption {
141       type = with types; nullOr (oneOf [ float int ]);
142       default = null;
143       description = lib.mdDoc ''
144         The P2 Time Window in seconds.
145         Both limit and window must be set.
146       '';
147     };
149     useTimer = mkOption {
150       type = types.bool;
151       default = false;
152       description = lib.mdDoc ''
153         Whether to set a timer that applies the undervolt settings every 30s.
154         This will cause spam in the journal but might be required for some
155         hardware under specific conditions.
156         Enable this if your undervolt settings don't hold.
157       '';
158     };
159   };
161   config = mkIf cfg.enable {
162     hardware.cpu.x86.msr.enable = true;
164     environment.systemPackages = [ cfg.package ];
166     systemd.services.undervolt = {
167       description = "Intel Undervolting Service";
169       # Apply undervolt on boot, nixos generation switch and resume
170       wantedBy = [ "multi-user.target" "post-resume.target" ];
171       after = [ "post-resume.target" ]; # Not sure why but it won't work without this
173       serviceConfig = {
174         Type = "oneshot";
175         Restart = "no";
176         ExecStart = "${cfg.package}/bin/undervolt ${toString cliArgs}";
177       };
178     };
180     systemd.timers.undervolt = mkIf cfg.useTimer {
181       description = "Undervolt timer to ensure voltage settings are always applied";
182       partOf = [ "undervolt.service" ];
183       wantedBy = [ "multi-user.target" ];
184       timerConfig = {
185         OnBootSec = "2min";
186         OnUnitActiveSec = "30";
187       };
188     };
189   };