// SPDX-License-Identifier: GPL-2.0 /* * Intel Performance and Energy Bias Hint support. * * Copyright (C) 2019 Intel Corporation * * Author: * Rafael J. Wysocki <[email protected]> */ #include <linux/cpuhotplug.h> #include <linux/cpu.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/syscore_ops.h> #include <linux/pm.h> #include <asm/cpu_device_id.h> #include <asm/cpufeature.h> #include <asm/msr.h> /** * DOC: overview * * The Performance and Energy Bias Hint (EPB) allows software to specify its * preference with respect to the power-performance tradeoffs present in the * processor. Generally, the EPB is expected to be set by user space (directly * via sysfs or with the help of the x86_energy_perf_policy tool), but there are * two reasons for the kernel to update it. * * First, there are systems where the platform firmware resets the EPB during * system-wide transitions from sleep states back into the working state * effectively causing the previous EPB updates by user space to be lost. * Thus the kernel needs to save the current EPB values for all CPUs during * system-wide transitions to sleep states and restore them on the way back to * the working state. That can be achieved by saving EPB for secondary CPUs * when they are taken offline during transitions into system sleep states and * for the boot CPU in a syscore suspend operation, so that it can be restored * for the boot CPU in a syscore resume operation and for the other CPUs when * they are brought back online. However, CPUs that are already offline when * a system-wide PM transition is started are not taken offline again, but their * EPB values may still be reset by the platform firmware during the transition, * so in fact it is necessary to save the EPB of any CPU taken offline and to * restore it when the given CPU goes back online at all times. * * Second, on many systems the initial EPB value coming from the platform * firmware is 0 ('performance') and at least on some of them that is because * the platform firmware does not initialize EPB at all with the assumption that * the OS will do that anyway. That sometimes is problematic, as it may cause * the system battery to drain too fast, for example, so it is better to adjust * it on CPU bring-up and if the initial EPB value for a given CPU is 0, the * kernel changes it to 6 ('normal'). */ static DEFINE_PER_CPU(u8, saved_epb); #define EPB_MASK … #define EPB_SAVED … #define MAX_EPB … enum energy_perf_value_index { … }; static u8 energ_perf_values[] = …; static int intel_epb_save(void) { … } static void intel_epb_restore(void) { … } static struct syscore_ops intel_epb_syscore_ops = …; static const char * const energy_perf_strings[] = …; static ssize_t energy_perf_bias_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t energy_perf_bias_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(energy_perf_bias); static struct attribute *intel_epb_attrs[] = …; static const struct attribute_group intel_epb_attr_group = …; static int intel_epb_online(unsigned int cpu) { … } static int intel_epb_offline(unsigned int cpu) { … } static const struct x86_cpu_id intel_epb_normal[] = …; static __init int intel_epb_init(void) { … } late_initcall(intel_epb_init);