ThinkPad T14 G2 AMD -> P14s G6 AMD

This commit is contained in:
Sergei Poljanski 2026-05-07 17:55:07 +03:00
commit 5fbe4e306f
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
13 changed files with 226 additions and 62 deletions

View file

@ -1,9 +1,31 @@
# Power management — ThinkPad T14 Gen 2 (AMD)
# Power management — ThinkPad P14s Gen 6 (AMD)
{ config, pkgs, lib, ... }:
{
# AMD P-State driver (active mode for full EPP control)
boot.kernelParams = [ "amd_pstate=active" ];
# resume_offset = first physical block of /var/swapfile (for hibernate from
# an ext4 swapfile on LUKS). Recompute if the swapfile is recreated:
# sudo filefrag -v /var/swapfile | awk 'NR==4{gsub(/\.\./,""); print $4}'
boot.kernelParams = [ "amd_pstate=active" "resume_offset=1769472" ];
# Hibernate / resume from encrypted swapfile on LUKS root.
# P14s G6 AMD has no S3 (Modern Standby only); this enables
# suspend-then-hibernate so long sleeps drain to disk.
boot.resumeDevice = "/dev/mapper/luks-71c97ce7-dd29-4b07-8d2a-8cc3985a65bd";
# systemd-logind: when lid closes / suspend pressed, suspend first;
# if still asleep after the delay, hibernate.
services.logind.settings.Login = {
HandleLidSwitch = "suspend-then-hibernate";
HandleSuspendKey = "suspend-then-hibernate";
IdleAction = "suspend-then-hibernate";
IdleActionSec = "30min";
};
# Switch to hibernate after this long in s2idle.
systemd.sleep.settings.Sleep = {
HibernateDelaySec = "45min";
};
# CPU frequency scaling
powerManagement.cpuFreqGovernor = "powersave";
@ -11,47 +33,93 @@
# Disable power-profiles-daemon (conflicts with TLP)
services.power-profiles-daemon.enable = false;
# TLP — main power management daemon
services.tlp = {
enable = true;
settings = {
# CPU
CPU_SCALING_GOVERNOR_ON_AC = "performance";
CPU_SCALING_GOVERNOR_ON_BAT = "powersave";
CPU_ENERGY_PERF_POLICY_ON_AC = "balance_performance";
CPU_ENERGY_PERF_POLICY_ON_BAT = "balance_power";
CPU_BOOST_ON_AC = 1;
CPU_BOOST_ON_BAT = 1;
PLATFORM_PROFILE_ON_AC = "balanced";
PLATFORM_PROFILE_ON_BAT = "balanced";
# TLP — settings owned by the TLP Profile Switcher GNOME extension.
# The extension does `pkexec cp ~/.tlp/<profile>.conf /etc/tlp.conf`, but
# NixOS makes /etc/tlp.conf a read-only symlink into the Nix store. We
# replace the symlink with a writable copy on every activation, AND if
# one of the user's ~/.tlp/*.conf matches the previous /etc/tlp.conf
# (= the user picked it from the panel), restore that one instead of the
# NixOS stub. This way the chosen profile survives nixos-rebuild switch.
services.tlp.enable = true;
system.activationScripts.tlpWritable = lib.stringAfter [ "etc" ] ''
profilesDir=/home/asxpi/.tlp
target=/etc/tlp.conf
activeMarker=/var/lib/tlp-active-profile
# AMD P-State
CPU_DRIVER_OPMODE_ON_AC = "active";
CPU_DRIVER_OPMODE_ON_BAT = "active";
# If the current file matches one of the user's profiles, remember it
# before NixOS replaces the symlink target.
if [ -f "$target" ] && [ ! -L "$target" ] && [ -d "$profilesDir" ]; then
currentSum=$(${pkgs.coreutils}/bin/sha256sum "$target" | ${pkgs.coreutils}/bin/cut -d' ' -f1)
for p in "$profilesDir"/*.conf; do
[ -f "$p" ] || continue
s=$(${pkgs.coreutils}/bin/sha256sum "$p" | ${pkgs.coreutils}/bin/cut -d' ' -f1)
if [ "$s" = "$currentSum" ]; then
${pkgs.coreutils}/bin/basename "$p" .conf > "$activeMarker"
break
fi
done
fi
# WiFi
WIFI_PWR_MGT_ON_AC = "off";
WIFI_PWR_MGT_ON_BAT = "on";
# Replace the read-only symlink with a writable real file.
if [ -L "$target" ]; then
cp --remove-destination "$(readlink -f "$target")" "$target"
chmod 0644 "$target"
fi
# PCIe ASPM
PCIE_ASPM_ON_AC = "default";
PCIE_ASPM_ON_BAT = "default";
# If we previously remembered an active profile, restore it.
if [ -f "$activeMarker" ]; then
active=$(cat "$activeMarker")
if [ -f "$profilesDir/$active.conf" ]; then
cp "$profilesDir/$active.conf" "$target"
chmod 0644 "$target"
fi
fi
'';
# Runtime PM
RUNTIME_PM_ON_AC = "on";
RUNTIME_PM_ON_BAT = "auto";
# NVMe
NVME_RUNTIME_PM_ON_AC = "on";
NVME_RUNTIME_PM_ON_BAT = "auto";
# USB autosuspend
USB_AUTOSUSPEND = 1;
# Hook the extension's cp: whenever /etc/tlp.conf changes via the panel,
# also update the marker. systemd.path watches the file mtime.
systemd.paths.tlp-active-profile-tracker = {
description = "Track which TLP profile is currently active";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "/etc/tlp.conf";
Unit = "tlp-active-profile-tracker.service";
};
};
systemd.services.tlp-active-profile-tracker = {
description = "Update /var/lib/tlp-active-profile to match /etc/tlp.conf";
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "tlp-track" ''
profilesDir=/home/asxpi/.tlp
target=/etc/tlp.conf
marker=/var/lib/tlp-active-profile
[ -f "$target" ] || exit 0
[ -d "$profilesDir" ] || exit 0
currentSum=$(${pkgs.coreutils}/bin/sha256sum "$target" | ${pkgs.coreutils}/bin/cut -d' ' -f1)
for p in "$profilesDir"/*.conf; do
[ -f "$p" ] || continue
s=$(${pkgs.coreutils}/bin/sha256sum "$p" | ${pkgs.coreutils}/bin/cut -d' ' -f1)
if [ "$s" = "$currentSum" ]; then
${pkgs.coreutils}/bin/basename "$p" .conf > "$marker"
exit 0
fi
done
'';
};
};
# Bluetooth off at boot
hardware.bluetooth.powerOnBoot = true;
hardware.bluetooth.settings = {
General = {
ControllerMode = "dual";
FastConnectable = true;
JustWorksRepairing = "always";
Privacy = "device";
Experimental = true;
};
};
# Battery monitoring
services.upower.enable = true;