diff --git a/nixos/modules/services/hardware/intel-lpmd.nix b/nixos/modules/services/hardware/intel-lpmd.nix
new file mode 100644
index 0000000000000..2ed0120175a0d
--- /dev/null
+++ b/nixos/modules/services/hardware/intel-lpmd.nix
@@ -0,0 +1,527 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.intel-lpmd;
+
+ perStateOptional = elem: if (elem != null) then elem else "-1";
+ xmlOptionalBool = elem: if elem then "1" else "0";
+
+ perStateGenerator =
+ cfg:
+ lib.concatStringsSep "\n" (
+ lib.forEach cfg (attr: ''
+
+ ${attr.ID}
+ ${attr.Name}
+ ${attr.EntrySystemLoadThres}
+ ${attr.EnterCPULoadThres}
+ ${attr.WLTType}
+ ${attr.ActiveCPUs}
+ ${perStateOptional attr.EPP}
+ ${perStateOptional attr.EPB}
+ ${perStateOptional attr.ITMTState}
+ ${perStateOptional attr.IRQMigrate}
+ ${attr.MinPollInterval}
+ ${
+ lib.optionalString (
+ attr.MaxPollInterval != null
+ ) "${attr.MaxPollInterval}"
+ }
+ ${perStateOptional attr.PollIntervalIncrement}
+
+ '')
+ );
+
+ statesGenerator =
+ cfg:
+ lib.concatStringsSep "\n" (
+ lib.forEach cfg (attr: ''
+
+ ${attr.CPUFamily}
+ ${attr.CPUModel}
+ ${attr.CPUConfig}
+ ${perStateGenerator attr.State}
+
+ '')
+ );
+
+ xmlGenerator =
+ cfg:
+ lib.concatStringsSep "\n" (
+ lib.forEach cfg (attr: ''
+
+
+ ${
+ lib.optionalString (attr.lp_mode_cpus != null) "${attr.lp_mode_cpus}"
+ }
+ ${attr.Mode}
+ ${attr.PerformanceDef}
+ ${attr.BalancedDef}
+ ${attr.PowersaverDef}
+ ${xmlOptionalBool attr.HfiLpmEable}
+ ${xmlOptionalBool attr.HfiSuvEnable}
+ ${xmlOptionalBool attr.WLTHintEnable}
+ ${xmlOptionalBool attr.WLTProxyEnable}
+ ${attr.util_entry_threshold}
+ ${attr.util_exit_threshold}
+ ${attr.EntryDelayMS}
+ ${attr.ExitDelayMS}
+ ${attr.EntryHystMS}
+ ${attr.ExitHystMS}
+ ${lib.optionalString (attr.lp_mode_epp != null) "${attr.lp_mode_epp}"}
+ ${xmlOptionalBool attr.IgnoreITMT}
+ ${statesGenerator attr.States}
+
+ '')
+ );
+
+ # TODO: more available in src/lpmd_config.c, lacking documentation
+ perStateSubmodule = {
+ options = {
+ ID = lib.mkOption {
+ description = ''
+ A unique ID for the state.
+ '';
+
+ type = with lib.types; int.unsigned;
+ };
+
+ Name = lib.mkOption {
+ description = ''
+ A name for the state.
+ '';
+
+ type = with lib.types; str;
+ };
+
+ EntrySystemLoadThres = lib.mkOption {
+ description = ''
+ System entry load threshold (in %). To enter this state,
+ system utilization must be less than or equal to this value.
+ '';
+
+ type = with lib.types; numbers.between 0 100;
+ };
+
+ EnterCPULoadThres = lib.mkOption {
+ description = ''
+ CPU entry load threshold (in %). To enter this state,
+ active CPU utilization must be less than or equal to this value.
+ '';
+
+ type = with lib.types; numbers.between 0 100;
+ };
+
+ WLTType = lib.mkOption {
+ description = ''
+ Workload type value to enter into this state. This will not work
+ without `WLTHintEnable`.
+ '';
+
+ type = with lib.types; int.unsigned; # TODO: range unclear
+ };
+
+ ActiveCPUs = lib.mkOption {
+ default = null;
+ description = ''
+ Active CPUs in this state. Leave as `null` to use all CPUs.
+
+ Can be specified as comma separating string,
+ or as a range by using '-'.
+ '';
+
+ type = with lib.types; nullOr str;
+ };
+
+ EPP = lib.mkOption {
+ default = null;
+ description = ''
+ EPP (energy performance preferences) to use for this state.
+ Leave as `null` to ignore this setting.
+
+ Accepts a value from **0** (favor performance)
+ to **255** (favor power).
+ '';
+
+ type = with lib.types; nullOr numbers.between 0 255;
+ };
+
+ EPB = lib.mkOption {
+ default = null;
+ description = ''
+ EPB (energy performance and bias) to apply for this state.
+ Leave as `null` to ignore this setting.
+
+ Accepts a value from **0** (highest performance)
+ to **15** (highest energy savings).
+ '';
+
+ type = with lib.types; nullOr numbers.between 0 15;
+ };
+
+ ITMTState = lib.mkOption {
+ default = null;
+ description = ''
+ Set the state of ITMT flag.
+ Leave as `null` to ignore.
+ '';
+
+ type = with lib.types; nullOr int.unsigned; # TODO: range unclear
+ };
+
+ IRQMigrate = lib.mkOption {
+ default = null;
+ description = ''
+ Migrate IRQs to the active CPUs in thi state.
+ Leave as `null` to ignore.
+ '';
+
+ type = with lib.types; nullOr int.unsigned; # TODO: range unclear
+ };
+
+ MinPollInterval = lib.mkOption {
+ description = ''
+ The minimum polling interval (in ms).
+ '';
+
+ type = with lib.types; int.unsigned;
+ };
+
+ MaxPollInterval = lib.mkOptional {
+ default = null;
+ description = ''
+ The maximum polling interval (in ms).
+ Leave as `null` to enforce no maximum.
+ '';
+
+ type = with lib.types; nullOr int.unsigned;
+ };
+
+ PollIntervalIncrement = lib.mkOption {
+ default = null;
+ description = ''
+ The polling interval increment (in ms).
+ Leave as `null` to let the daemon configure this based on CPU utilization.
+ '';
+
+ type = with lib.types; nullOr int.unsigned;
+ };
+ };
+ };
+
+ statesSubmodule = {
+ options = {
+ CPUFamily = lib.mkOption {
+ description = ''
+ The CPU generation to match.
+ '';
+
+ type = with lib.types; str;
+ };
+
+ CPUModel = lib.mkOption {
+ description = ''
+ The CPU model to match.
+ '';
+
+ type = with lib.types; str;
+ };
+
+ CPUConfig = lib.mkOption {
+ description = ''
+ Define a configuration of CPUs and TDP to match different skews
+ for the same CPU model and family.
+
+ See `man 5 intel_lpmd_config.xml` for more details.
+ '';
+
+ type = with lib.types; str;
+ };
+
+ State = lib.mkOption {
+ default = [ ];
+ description = ''
+ List of "state" definitions.
+ '';
+
+ type = with lib.types; listOf (submodule perStateSubmodule);
+ };
+ };
+ };
+
+ xmlSubmodule = {
+ options = {
+ lp_mode_cpus = lib.mkOption {
+ default = null;
+ description = ''
+ The set of active CPUs when in LPM (low power mode).
+ Leave as `null` to let the daemon auto-detect this.
+
+ Can be specified as comma separating string, or as a range by using
+ '..' or '-'.
+ '';
+
+ example = "1,2,4..6,8-10";
+
+ type = with lib.types; nullOr str;
+ };
+
+ Mode = lib.mkOption {
+ default = "0";
+ description = ''
+ Specifies the way to migrate tasks to active CPUs
+ when LPM is active.
+
+ - '0': set *cpuset* to the active CPUs for cgroup v2 based systemd
+ - '1': isolate non-LPM CPUs so tasks are scheduled for LPM CPUs only
+ - '2': force idle injection to non-LPM CPUs and let scheduler handle LPM CPUs
+ '';
+
+ type =
+ with lib.types;
+ enum [
+ "0"
+ "1"
+ "2"
+ ];
+ };
+
+ PerformanceDef = lib.mkOption {
+ default = "-1";
+ description = ''
+ Specifies the default behaviour when the power setting
+ is set to **Performance**.
+
+ - '-1': never enter LPM
+ - '0': opportunistic LPM enter/exit based of HFI/Util request
+ - '1': always stay in LPM
+ '';
+
+ type =
+ with lib.types;
+ enum [
+ "-1"
+ "0"
+ "1"
+ ];
+ };
+
+ BalancedDef = lib.mkOption {
+ default = "-1";
+ description = ''
+ Specifies the default behaviour when the power setting
+ is set to **Balanced**.
+
+ - '-1': never enter LPM
+ - '0': opportunistic LPM enter/exit based of HFI/Util request
+ - '1': always stay in LPM
+ '';
+
+ type =
+ with lib.types;
+ enum [
+ "-1"
+ "0"
+ "1"
+ ];
+ };
+
+ PowersaverDef = lib.mkOption {
+ default = "-1";
+ description = ''
+ Specifies the default behaviour when the power setting
+ is set to **Power saver**.
+
+ - '-1': never enter LPM
+ - '0': opportunistic LPM enter/exit based of HFI/Util request
+ - '1': always stay in LPM
+ '';
+
+ type =
+ with lib.types;
+ enum [
+ "-1"
+ "0"
+ "1"
+ ];
+ };
+
+ HfiLpmEnable = {
+ default = false;
+ description = ''
+ Specifies if the HFI monitor can capture HFI hints for LPM.
+ '';
+
+ type = with lib.types; bool;
+ };
+
+ HfiSuvEnable = {
+ default = false;
+ description = ''
+ Specifies if the HFI monitor can capture HFI hints for survivability mode.
+ '';
+
+ type = with lib.types; bool;
+ };
+
+ WLTHintEnable = lib.mkOption {
+ default = false;
+ description = ''
+ Enable use of hardware workload type hints.
+ '';
+
+ type = with lib.types; bool;
+ };
+
+ WLTProxyEnable = lib.mkOption {
+ default = false;
+ description = ''
+ Enable use of proxy workload type hints.
+ '';
+
+ type = with lib.types; bool;
+ };
+
+ util_entry_threshold = lib.mkOption {
+ default = 10;
+ description = ''
+ Specifices the system utilization threshold (in %) for entering LPM.
+ Set both this and `util_exit_threshold` to '0' to disable this feature.
+ '';
+
+ type = with lib.types; numbers.between 0 100;
+ };
+
+ util_exit_threshold = lib.mkOption {
+ default = 95;
+ description = ''
+ Specifices the system utilization threshold (in %) for exiting LPM.
+ Set both this and `util_entry_threshold` to '0' to disable this feature.
+ '';
+
+ type = with lib.types; numbers.between 0 100;
+ };
+
+ EntryDelayMS = lib.mkOption {
+ default = 0;
+ description = ''
+ Specifies the sample interval (in ms) used by the utilization monitor
+ when system wants to enter LPM.
+
+ Setting this to '0' uses the default interval of 1000ms.
+ '';
+
+ type = with lib.types; ints.unsigned;
+ };
+
+ ExitDelayMS = lib.mkOption {
+ default = 0;
+ description = ''
+ Specifies the sample interval (in ms) used by the utilization monitor
+ when system wants to exit LPM.
+
+ Setting this to '0' uses an adaptive value which is calculated based
+ on CPU utilization.
+ '';
+
+ type = with lib.types; ints.unsigned;
+ };
+
+ # TODO: it is unclear if disabling requires both to be set to '0' or not.
+ EntryHystMS = lib.mkOption {
+ default = "0";
+ description = ''
+ Specifies a hysteresis threshold (in ms) when the system is in LPM.
+ When the previous average time **stayed in** LPM is lower than this
+ value, the **current enter** LPM request will be ignored, due to the
+ expectation of the system exiting LPM soon.
+
+ Setting this to '0' disables the hysteresis algorithm.
+ '';
+
+ type = with lib.types; ints.unsigned;
+ };
+
+ ExitHystMS = lib.mkOption {
+ default = "0";
+ description = ''
+ Specifies a hysteresis threshold (in ms) when the system is not in LPM.
+ When the previous average time **stayed out of** LPM is lower than this
+ value, the **current exit** LPM request will be ignored, due to the
+ expectation of the system entering LPM soon.
+
+ Setting this to '0' disables the hysteresis algorithm.
+ '';
+
+ type = with lib.types; ints.unsigned;
+ };
+
+ lp_mode_epp = lib.mkOption {
+ default = null;
+ description = ''
+ EPP (energy performance preferences) to use in LPM.
+ Leave as `null` to ignore this setting.
+
+ Accepts a value from **0** (favor performance)
+ to **255** (favor power).
+ '';
+
+ type = with lib.types; nullOr numbers.between 0 255;
+ };
+
+ IgnoreITMT = lib.mkOption {
+ default = false;
+ description = ''
+ Specifies whether to avoid changing the scheduler ITMT flag.
+
+ If enabled, ITMT is disabled upon entering LPM,
+ and re-enabled upon exiting LPM. Otherwise, the
+ ITMT setting is ignored entirely.
+ '';
+ };
+
+ States = lib.mkOption {
+ default = [ ];
+ description = ''
+ List of per-platform low power states.
+ '';
+
+ type = with lib.types; listOf (submodule statesSubmodule);
+ };
+ };
+ };
+in
+{
+ ###### interface
+ options = {
+ services.intel-lpmd = {
+ enable = lib.mkEnableOption "Intel's low power mode daemon";
+ package = lib.mkPackageOption pkgs "intel-lpmd";
+
+ settings = lib.mkOption {
+ default = { };
+ description = ''
+ Configuration for the daemon, written to `/etc/intel_lpmd`.
+ See `man 5 intel_lpmd_config.xml` for available configuration.
+ '';
+
+ type = with lib.types; attrsOf (submodule xmlSubmodule);
+ };
+ };
+ };
+
+ ###### implementation
+ config = lib.mkIf cfg.enable {
+ environment = {
+ etc."/etc/intel_lpmd/intel_lpmd_config.xml".text = xmlGenerator cfg;
+ systemPackages = [ cfg.package ];
+ };
+
+ services.dbus.packages = [ cfg.package ];
+ systemd.packages = [ cfg.package ];
+ };
+}
diff --git a/pkgs/by-name/in/intel-lpmd/package.nix b/pkgs/by-name/in/intel-lpmd/package.nix
new file mode 100644
index 0000000000000..e390a91ae77cb
--- /dev/null
+++ b/pkgs/by-name/in/intel-lpmd/package.nix
@@ -0,0 +1,76 @@
+{
+ lib,
+ stdenv,
+ fetchFromGitHub,
+
+ autoreconfHook,
+ pkg-config,
+
+ glib,
+ gtk-doc,
+ libnl,
+ libxml2,
+ systemd,
+ upower,
+
+ coreutils,
+}:
+stdenv.mkDerivation (finalAttrs: {
+ pname = "intel-lpmd";
+ version = "0.0.8";
+
+ src = fetchFromGitHub {
+ owner = "intel";
+ repo = "intel-lpmd";
+ rev = "refs/tags/v${finalAttrs.version}";
+ hash = "sha256-Af8H+hX9S7+AlFxFvClsRgEgt+bYqy9T+IWUkbUPVEw=";
+ };
+
+ nativeBuildInputs = [
+ autoreconfHook
+ pkg-config
+
+ glib
+ gtk-doc
+ libnl
+ libxml2
+ systemd
+ upower
+ ];
+
+ postPatch = ''
+ substituteInPlace "data/org.freedesktop.intel_lpmd.service.in" \
+ --replace-fail "/bin/false" "${lib.getExe' coreutils "false"}"
+
+ substituteInPlace "src/lpmd_dbus_server.c" \
+ --replace-fail "src/intel_lpmd_dbus_interface.xml" "${placeholder "out"}/share/dbus-1/interfaces/org.freedesktop.intel_lpmd.xml"
+ '';
+
+ configureFlags = [
+ ''--with-dbus-sys-dir="${placeholder "out"}/share/dbus-1/system.d"''
+ ''--with-systemdsystemunitdir="${placeholder "out"}/lib/systemd/system"''
+ ];
+
+ postInstall = ''
+ install -Dm644 src/intel_lpmd_dbus_interface.xml $out/share/dbus-1/interfaces/org.freedesktop.intel_lpmd.xml
+ '';
+
+ meta = with lib; {
+ homepage = "https://github.com/intel/intel-lpmd";
+ description = "Linux daemon used to optimize active idle power.";
+ longDescription = ''
+ Intel Low Power Model Daemon is a Linux daemon used to optimize active
+ idle power. It selects a set of most power efficient CPUs based on
+ configuration file or CPU topology. Based on system utilization and other
+ hints, it puts the system into Low Power Mode by activate the power
+ efficient CPUs and disable the rest, and restore the system from Low Power
+ Mode by activating all CPUs.
+ '';
+
+ platforms = platforms.linux;
+ license = licenses.gpl2Only;
+ maintainers = with maintainers; [ frontear ];
+
+ mainProgram = "intel_lpmd";
+ };
+})