amd_pstate Driver
amd_pstateis AMD’s dedicated CPU-frequency scaling driver, the structural twin of intel_pstate but built on the open ACPI standard CPPC (Collaborative Processor Performance Control) rather than Intel’s proprietary HWP MSRs. It offers three operation modes:passive(a normaltarget/fast_switchdriver under a generic governor such as schedutil),active(the autonomousamd-pstate-eppdriver that bypasses the governor layer and feeds the hardware an Energy Performance Preference hint), andguided(a hybrid where software sets a min/max window and hardware autonomously picks within it). CPPC exposes four abstract performance levels — highest, nominal, lowest_nonlinear, and lowest — that the driver maps to frequencies. Verified againstdrivers/cpufreq/amd-pstate.c,amd-pstate.h, andKconfig.x86at thev6.12tag (2024-11-17): the compiled-in default mode is active (EPP) viaCONFIG_X86_AMD_PSTATE_DEFAULT_MODE=3, but the driver declines to load on server and undefined-ACPI-profile platforms unless an explicitamd_pstate=boot parameter is given.
For Intel’s near-identical driver and the shared concepts (P-states, active vs passive, EPP), see intel_pstate and Hardware-Managed P-States; for the layer both plug into, see The cpufreq Subsystem and cpufreq Scaling Drivers.
The Default-Mode Question (resolved against v6.12 source)
Because the parent Linux Power Management MOC explicitly flags the amd_pstate default as fast-moving and uncertain, this is settled first, against the source rather than from memory.
The global mode variable starts undefined: static int cppc_state = AMD_PSTATE_UNDEFINED; (v6.12, amd-pstate.c). In amd_pstate_init(), when no boot parameter has set the mode, the driver runs this exact logic (v6.12):
if (cppc_state == AMD_PSTATE_UNDEFINED) {
/* Disable on the following configs by default:
* 1. Undefined platforms
* 2. Server platforms
*/
if (amd_pstate_acpi_pm_profile_undefined() ||
amd_pstate_acpi_pm_profile_server()) {
pr_info("driver load is disabled, boot with specific mode to enable this\n");
return -ENODEV;
}
/* get driver mode from kernel config option [1:4] */
cppc_state = CONFIG_X86_AMD_PSTATE_DEFAULT_MODE;
}amd_pstate_acpi_pm_profile_server() returns true for ACPI FADT preferred-profile values PM_ENTERPRISE_SERVER, PM_SOHO_SERVER, or PM_PERFORMANCE_SERVER. So:
- On a server (or a machine with an undefined ACPI power profile),
amd_pstatereturns-ENODEVand does not load unless you boot withamd_pstate=passive|active|guided. The platform stays onacpi-cpufreq. - On a client (laptop/desktop), the mode is
CONFIG_X86_AMD_PSTATE_DEFAULT_MODE. FromKconfig.x86at v6.12:default 3 if X86_AMD_PSTATE, withrange 1 4and the documented meanings1 -> Disabled, 2 -> Passive, 3 -> Active (EPP), 4 -> Guided.3= active (EPP).
This matches the upstream history: the active/EPP default for client Zen 2 and newer with CPPC landed in Linux 6.5 (2023), replacing the earlier passive default (Phoronix, “Linux 6.5 Now Defaults To AMD P-State Active EPP”).
Uncertain
Verify: that the built kernel on a given distribution actually uses
CONFIG_X86_AMD_PSTATE_DEFAULT_MODE=3, and the precise CPU/firmware preconditions for the driver to auto-bind (CPPC present, not a shared-memory-only design on some configs,X86_FEATURE_CPPCetc.). Reason: the upstream default is=3(verified in v6.12Kconfig.x86), but the effective default is a per-distroKconfigchoice and depends on firmware advertising_CPC; real users have reported getting passive instead of active despite the documented default. To resolve: check the distro’s/boot/config-$(uname -r)forCONFIG_X86_AMD_PSTATE_DEFAULT_MODE, and readscaling_driver+amd_pstate/statuson the target machine. The Arch forum thread (bbs.archlinux.org id=288419) documents exactly this divergence. uncertain
Mental Model
CPPC is the key abstraction. Where Intel’s HWP exposes raw 0–255 performance values in proprietary MSRs, CPPC is an ACPI-standard interface (defined in the ACPI spec) that any vendor can implement. AMD implements it in one of two ways, and amd_pstate adapts to whichever is present:
- Full / MSR CPPC (Zen 3 “Cezanne” and newer with the
X86_FEATURE_CPPCflag): performance levels and requests live in dedicated MSRs the kernel reads/writes directly. This is fast enough to use from the scheduler’s fast-switch path (interrupt context), giving low-latency frequency control. - Shared-memory CPPC (older parts): the same fields live in an ACPI mailbox region accessed via the
cppc_acpihelper layer — functionally identical but slower, so no fast-switch.
The driver abstracts the two behind static-call indirection: at init it picks the MSR (pstate_*) or shared-memory (cppc_*) implementation. The mode (passive/active/guided) is an orthogonal axis on top of that.
flowchart TB subgraph MODES["amd_pstate operation modes (one chosen at registration)"] PASS["passive<br/>driver: amd-pstate<br/>.target / .fast_switch / .adjust_perf<br/>governor (schedutil) drives it"] ACT["active (EPP)<br/>driver: amd-pstate-epp<br/>.setpolicy, no governor<br/>writes EPP hint to hardware"] GUID["guided<br/>driver: amd-pstate<br/>kernel sets min/max window<br/>des_perf=0 -> hardware picks"] end subgraph CPPC["CPPC interface (chosen by hardware capability)"] MSR["Full/MSR CPPC<br/>X86_FEATURE_CPPC<br/>MSR_AMD_CPPC_CAP1 / _REQ"] SHM["Shared-memory CPPC<br/>ACPI mailbox via cppc_acpi"] end subgraph LEVELS["CPPC performance levels (read-only caps)"] HI["highest_perf (peak, transient)"] NOM["nominal_perf (sustainable ~base)"] LNL["lowest_nonlinear_perf"] LO["lowest_perf (slowest)"] end MODES --> CPPC --> LEVELS
The two orthogonal axes of amd_pstate. What it shows: the mode (top) decides who picks the frequency — a generic governor (passive), the hardware via EPP hint (active), or a hardware-within-software-bounds hybrid (guided); the CPPC variant (middle) decides the transport (fast MSRs vs slow ACPI mailbox); both resolve to the same four read-only performance caps (bottom). The insight to take: mode and transport are independent — you can run active mode on a shared-memory part or passive mode on an MSR part. The four performance levels are the common vocabulary all of it is expressed in.
Mechanical Walk-through
Reading the CPPC performance levels (verified at v6.12)
The four levels come from MSR_AMD_CPPC_CAP1 (0xc00102b0) on MSR-capable parts, decoded with macros from arch/x86/include/asm/msr-index.h (v6.12):
#define AMD_CPPC_LOWEST_PERF(x) (((x) >> 0) & 0xff)
#define AMD_CPPC_LOWNONLIN_PERF(x) (((x) >> 8) & 0xff)
#define AMD_CPPC_NOMINAL_PERF(x) (((x) >> 16) & 0xff)
#define AMD_CPPC_HIGHEST_PERF(x) (((x) >> 24) & 0xff)pstate_init_perf() (v6.12) reads the MSR and stashes the four values; cppc_init_perf() does the same via cppc_get_perf_caps() on shared-memory parts. The four levels mean (per amd-pstate.rst):
- Highest performance — the absolute maximum an individual core may reach “assuming ideal conditions,” typically requiring other cores idle and thermal headroom; the peak (boost) frequency.
- Nominal (guaranteed) performance — “the maximum sustained performance level of the processor, assuming ideal operating conditions,” i.e. the base frequency that can be held across all cores indefinitely.
- Lowest nonlinear performance — “the lowest performance level at which nonlinear power savings are achieved.” Below this, dropping frequency further keeps cutting power but the energy per unit of work stops improving (you spend longer at low frequency). This is the practical floor for efficiency.
- Lowest performance — the absolute slowest the CPU will run; going below
lowest_nonlineartrades efficiency for the lowest instantaneous power draw.
These are abstract 0–255 values; the driver maps them to frequencies (cpuinfo_max_freq, amd_pstate_max_freq, amd_pstate_lowest_nonlinear_freq in sysfs).
How the mode selects the driver and the request encoding
amd_pstate_register_driver() (v6.12) maps the resolved cppc_state to a cpufreq_driver:
if (mode == AMD_PSTATE_PASSIVE || mode == AMD_PSTATE_GUIDED)
current_pstate_driver = &amd_pstate_driver; /* name "amd-pstate" */
else if (mode == AMD_PSTATE_ACTIVE)
current_pstate_driver = &amd_pstate_epp_driver; /* name "amd-pstate-epp" */So passive and guided share the amd-pstate driver (with .target/.fast_switch/.adjust_perf — it cooperates with a generic governor), while active uses the separate amd-pstate-epp driver (with .setpolicy and no .target, bypassing the governor exactly like intel_pstate active mode). The full enum (v6.12, amd-pstate.h):
enum amd_pstate_mode {
AMD_PSTATE_UNDEFINED = 0,
AMD_PSTATE_DISABLE, /* 1 */
AMD_PSTATE_PASSIVE, /* 2 */
AMD_PSTATE_ACTIVE, /* 3 */
AMD_PSTATE_GUIDED, /* 4 */
AMD_PSTATE_MAX,
};Requests are packed into MSR_AMD_CPPC_REQ (0xc00102b3) with these field macros (v6.12 msr-index.h):
#define AMD_CPPC_MAX_PERF(x) (((x) & 0xff) << 0)
#define AMD_CPPC_MIN_PERF(x) (((x) & 0xff) << 8)
#define AMD_CPPC_DES_PERF(x) (((x) & 0xff) << 16)
#define AMD_CPPC_ENERGY_PERF_PREF(x)(((x) & 0xff) << 24)The desired field (des_perf) is the lever that distinguishes the modes. In amd_pstate_update() (v6.12), guided mode is handled specially:
if ((cppc_state == AMD_PSTATE_GUIDED) &&
(gov_flags & CPUFREQ_GOV_DYNAMIC_SWITCHING)) {
min_perf = des_perf; /* floor the window at the governor's desired point */
des_perf = 0; /* 0 = "no fixed target, hardware decides" */
}That is the mechanical heart of the three modes, all expressed through one request register:
- Passive: software computes
des_perffrom the governor and writes min/max/desired — the kernel names a target. - Guided: software sets the window (
min = desired,max = highest) and writesdes_perf = 0, telling the hardware “pick autonomously inside this window.” A middle ground: software sets bounds, hardware does the fast moment-to-moment selection. - Active (EPP): the
amd-pstate-eppdriver sets a broad window once and relies on the EPP field to bias the hardware’s autonomous choice — the most hands-off mode, mirroring HWP.
EPP on AMD
Active mode exposes the same five symbolic EPP strings as Intel — no coincidence, the sysfs interface was deliberately kept compatible. From amd-pstate.c (v6.12):
static const char * const energy_perf_strings[] = {
[EPP_INDEX_DEFAULT] = "default",
[EPP_INDEX_PERFORMANCE] = "performance",
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
[EPP_INDEX_POWERSAVE] = "power",
};The EPP value (bits 31:24 of MSR_AMD_CPPC_REQ) ranges 0 (bias performance) to 0xff (bias efficiency); default means “whatever firmware set.” The epp_cached, epp_policy, and epp_default fields in struct amd_cpudata (v6.12 amd-pstate.h) track the requested, applied, and firmware-default values respectively.
Preferred-core ranking
A capability amd_pstate adds beyond mode/EPP is preferred core: CPPC reports a per-core performance ranking (some cores in a chiplet can hit higher frequencies at lower voltage due to silicon variation), surfaced as amd_pstate_prefcore_ranking and amd_pstate_hw_prefcore in sysfs. The scheduler uses this to prefer placing work on the “best” cores. Rankings are dynamic (they shift with thermals/workload) and can be disabled with amd_prefcore=disable. This ties amd_pstate into scheduler placement, a coupling Intel’s driver does not have in the same form.
Configuration and Diagnostics
Check the mode and driver binding:
$ cat /sys/devices/system/cpu/amd_pstate/status
active # global driver mode: active / passive / guided / disable
$ cat /sys/devices/system/cpu/cpufreq/policy0/scaling_driver
amd-pstate-epp # "amd-pstate-epp" = active; "amd-pstate" = passive or guided
$ cat /sys/devices/system/cpu/cpufreq/policy0/energy_performance_preference
balance_performance
$ cat /sys/devices/system/cpu/cpufreq/policy0/energy_performance_available_preferences
default performance balance_performance balance_power power
$ cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_highest_perf
166
$ cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_lowest_nonlinear_freq
1115000Note scaling_driver distinguishes active (amd-pstate-epp) from passive/guided (amd-pstate); to tell passive from guided you must read the global amd_pstate/status. The amd_pstate_highest_perf value (166 here) is the raw CPPC highest-performance number, not a frequency.
Switch mode at runtime (no reboot):
# echo passive > /sys/devices/system/cpu/amd_pstate/status
# echo guided > /sys/devices/system/cpu/amd_pstate/status
# echo active > /sys/devices/system/cpu/amd_pstate/statusForce a mode (or force-enable on a server) at boot — note server platforms need this to load at all:
amd_pstate=active # or =passive / =guided / =disable, parsed by the early_param handler
Tune EPP for efficiency on one policy (active mode):
# echo power > /sys/devices/system/cpu/cpufreq/policy0/energy_performance_preferenceFailure Modes and Common Misunderstandings
“amd_pstate isn’t loading on my EPYC server.” By design — verified above, the driver returns -ENODEV on server ACPI profiles unless you pass amd_pstate=active|passive|guided. The machine sits on acpi-cpufreq until you do. This is the single most common surprise.
“The docs say active is default but I got passive.” The upstream Kconfig default is =3 (active), but the effective default is whatever your distro compiled CONFIG_X86_AMD_PSTATE_DEFAULT_MODE as, plus firmware preconditions (the platform must advertise _CPC in ACPI). Check /boot/config-$(uname -r). This exact divergence is documented in the Arch forums (id=288419) — see the uncertainty callout above.
“No CPPC, falls back to acpi-cpufreq.” If _CPC is absent from ACPI (older firmware, or a BIOS with CPPC disabled), amd_pstate cannot initialize and the kernel uses acpi-cpufreq with a generic governor. Some boards hide CPPC behind a BIOS option; enabling it is the fix.
Confusing lowest_nonlinear with lowest. Setting the floor at lowest_perf saves the most instantaneous power but is often less energy-efficient for real work, because the CPU runs so slowly it stays busy longer (the “race-to-idle” trade). lowest_nonlinear is the efficiency sweet spot; below it you are buying power reduction at the cost of energy-per-task.
Shared-memory parts feel less responsive. Without X86_FEATURE_CPPC, requests go through the ACPI mailbox, which is too slow for the scheduler fast-switch path — expect slightly coarser frequency control than on MSR-capable Zen 3+ parts.
Alternatives and When to Choose Them
amd_pstateactive vs passive vs guided. Active (default on client) is the most efficient hands-off choice — hardware reacts in microseconds, biased by EPP. Passive gives you a specific generic governor (e.g.schedutil) and software control of the target; pick it for cross-vendor uniformity or when a governor’s behavior is required. Guided is the middle ground — software bounds, hardware autonomy — useful when you want autonomous responsiveness but a software-enforced ceiling/floor.acpi-cpufreq. The generic ACPI_PSSdriver and the fallback whenamd_pstatedeclines (servers without the boot param, no-CPPC firmware). It exposes only the discrete ACPI P-states rather than CPPC’s fine-grained continuous range, so it is coarser. See cpufreq Scaling Drivers.- intel_pstate. The Intel counterpart. The big differences:
amd_pstateis built on the open CPPC standard (so the same code path serves shared-memory parts), adds a third guided mode and preferred-core ranking, whereasintel_pstateuses proprietary HWP MSRs andhwp_dynamic_boost. Both share the active/passive split and the five EPP strings.
Production Notes
The Linux 6.5 switch to active/EPP-by-default for client Zen 2+ was a deliberate efficiency win: AMD’s data and independent testing showed the EPP path delivering better performance-per-watt than acpi-cpufreq + ondemand, with the hardware reacting faster than a software governor (Phoronix). Distributions on 6.12 LTS inherit this — a current Fedora/Ubuntu/Arch desktop on a Ryzen laptop is almost certainly running amd-pstate-epp with balance_performance EPP out of the box, which is why battery life improved noticeably across that kernel transition.
The server exclusion is intentional and worth internalizing for fleet operators: data-center AMD parts stay on acpi-cpufreq unless amd_pstate= is set, because the autonomous EPP behavior was not (at the time of the default change) validated for the steady, predictable load profiles servers want — and AMD preferred operators opt in explicitly. If you run EPYC and want CPPC’s fine-grained control, you must add the boot parameter and then choose your mode (often passive + schedutil for predictability, or guided for autonomy with a ceiling).
Preferred-core ranking is the AMD-specific production knob that surprises people: on chiplet parts, watching amd_pstate_prefcore_ranking shift across cores under sustained load is normal — it reflects the hardware steering single-threaded bursts onto the best-binned cores, and is exactly what feeds the scheduler’s placement decision.
See Also
- intel_pstate and Hardware-Managed P-States — the Intel counterpart this driver mirrors; same active/passive split and EPP strings, different (proprietary HWP) transport
- cpufreq Scaling Drivers — the driver layer and the generic
acpi-cpufreqfallbackamd_pstatedeclines into - The cpufreq Subsystem — the core policy model and the
.target/.setpolicy/.fast_switchcallbacks this driver implements - The schedutil Governor — the generic governor that drives
amd_pstatepassive and guided modes - Energy-Aware Scheduling EAS — consumes the preferred-core ranking
amd_pstateexposes - Linux Power Management MOC — the parent map (§2, CPU Frequency Scaling), which flags this driver’s default mode as fast-moving