1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.undervolt;
7in {
8 options.services.undervolt = {
9 enable = mkOption {
10 type = types.bool;
11 default = false;
12 description = ''
13 Whether to undervolt intel cpus.
14 '';
15 };
16
17 verbose = mkOption {
18 type = types.bool;
19 default = false;
20 description = ''
21 Whether to enable verbose logging.
22 '';
23 };
24
25 package = mkOption {
26 type = types.package;
27 default = pkgs.undervolt;
28 defaultText = "pkgs.undervolt";
29 description = ''
30 undervolt derivation to use.
31 '';
32 };
33
34 coreOffset = mkOption {
35 type = types.nullOr types.str;
36 default = null;
37 description = ''
38 The amount of voltage to offset the CPU cores by. Accepts a floating point number.
39 '';
40 };
41
42 gpuOffset = mkOption {
43 type = types.nullOr types.str;
44 default = null;
45 description = ''
46 The amount of voltage to offset the GPU by. Accepts a floating point number.
47 '';
48 };
49
50 uncoreOffset = mkOption {
51 type = types.nullOr types.str;
52 default = null;
53 description = ''
54 The amount of voltage to offset uncore by. Accepts a floating point number.
55 '';
56 };
57
58 analogioOffset = mkOption {
59 type = types.nullOr types.str;
60 default = null;
61 description = ''
62 The amount of voltage to offset analogio by. Accepts a floating point number.
63 '';
64 };
65
66 temp = mkOption {
67 type = types.nullOr types.str;
68 default = null;
69 description = ''
70 The temperature target. Accepts a floating point number.
71 '';
72 };
73
74 tempAc = mkOption {
75 type = types.nullOr types.str;
76 default = null;
77 description = ''
78 The temperature target on AC power. Accepts a floating point number.
79 '';
80 };
81
82 tempBat = mkOption {
83 type = types.nullOr types.str;
84 default = null;
85 description = ''
86 The temperature target on battery power. Accepts a floating point number.
87 '';
88 };
89 };
90
91 config = mkIf cfg.enable {
92 boot.kernelModules = [ "msr" ];
93
94 environment.systemPackages = [ cfg.package ];
95
96 systemd.services.undervolt = {
97 path = [ pkgs.undervolt ];
98
99 description = "Intel Undervolting Service";
100 serviceConfig = {
101 Type = "oneshot";
102 Restart = "no";
103
104 # `core` and `cache` are both intentionally set to `cfg.coreOffset` as according to the undervolt docs:
105 #
106 # Core or Cache offsets have no effect. It is not possible to set different offsets for
107 # CPU Core and Cache. The CPU will take the smaller of the two offsets, and apply that to
108 # both CPU and Cache. A warning message will be displayed if you attempt to set different offsets.
109 ExecStart = ''
110 ${pkgs.undervolt}/bin/undervolt \
111 ${optionalString cfg.verbose "--verbose"} \
112 ${optionalString (cfg.coreOffset != null) "--core ${cfg.coreOffset}"} \
113 ${optionalString (cfg.coreOffset != null) "--cache ${cfg.coreOffset}"} \
114 ${optionalString (cfg.gpuOffset != null) "--gpu ${cfg.gpuOffset}"} \
115 ${optionalString (cfg.uncoreOffset != null) "--uncore ${cfg.uncoreOffset}"} \
116 ${optionalString (cfg.analogioOffset != null) "--analogio ${cfg.analogioOffset}"} \
117 ${optionalString (cfg.temp != null) "--temp ${cfg.temp}"} \
118 ${optionalString (cfg.tempAc != null) "--temp-ac ${cfg.tempAc}"} \
119 ${optionalString (cfg.tempBat != null) "--temp-bat ${cfg.tempBat}"}
120 '';
121 };
122 };
123
124 systemd.timers.undervolt = {
125 description = "Undervolt timer to ensure voltage settings are always applied";
126 partOf = [ "undervolt.service" ];
127 wantedBy = [ "multi-user.target" ];
128 timerConfig = {
129 OnBootSec = "2min";
130 OnUnitActiveSec = "30";
131 };
132 };
133 };
134}