If control status is "forceoff" or "notsupported" writes
                         are rejected.
+
+What:          /sys/devices/system/cpu/cpu#/power/energy_perf_bias
+Date:          March 2019
+Contact:       linux-pm@vger.kernel.org
+Description:   Intel Energy and Performance Bias Hint (EPB)
+
+               EPB for the given CPU in a sliding scale 0 - 15, where a value
+               of 0 corresponds to a hint preference for highest performance
+               and a value of 15 corresponds to the maximum energy savings.
+
+               In order to change the EPB value for the CPU, write either
+               a number in the 0 - 15 sliding scale above, or one of the
+               strings: "performance", "balance-performance", "normal",
+               "balance-power", "power" (that represent values reflected by
+               their meaning), to this attribute.
+
+               This attribute is present for all online CPUs supporting the
+               Intel EPB feature.
 
 
 .. kernel-doc:: arch/x86/kernel/cpu/intel_epb.c
    :doc: overview
+
+Intel Performance and Energy Bias Attribute in ``sysfs``
+========================================================
+
+The Intel Performance and Energy Bias Hint (EPB) value for a given (logical) CPU
+can be checked or updated through a ``sysfs`` attribute (file) under
+:file:`/sys/devices/system/cpu/cpu<N>/power/`, where the CPU number ``<N>``
+is allocated at the system initialization time:
+
+``energy_perf_bias``
+       Shows the current EPB value for the CPU in a sliding scale 0 - 15, where
+       a value of 0 corresponds to a hint preference for highest performance
+       and a value of 15 corresponds to the maximum energy savings.
+
+       In order to update the EPB value for the CPU, this attribute can be
+       written to, either with a number in the 0 - 15 sliding scale above, or
+       with one of the strings: "performance", "balance-performance", "normal",
+       "balance-power", "power" that represent values reflected by their
+       meaning.
+
+       This attribute is present for all online CPUs supporting the EPB
+       feature.
+
+Note that while the EPB interface to the processor is defined at the logical CPU
+level, the physical register backing it may be shared by multiple CPUs (for
+example, SMT siblings or cores in one package).  For this reason, updating the
+EPB value for one CPU may cause the EPB values for other CPUs to change.
 
  */
 
 #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/cpufeature.h>
 #include <asm/msr.h>
  *
  * 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 through
- * the generic MSR interface (with the help of the x86_energy_perf_policy tool),
- * but there are two reasons for the kernel to touch it.
+ * 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
 
 #define EPB_MASK       0x0fULL
 #define EPB_SAVED      0x10ULL
+#define MAX_EPB                EPB_MASK
 
 static int intel_epb_save(void)
 {
        .resume = intel_epb_restore,
 };
 
+static const char * const energy_perf_strings[] = {
+       "performance",
+       "balance-performance",
+       "normal",
+       "balance-power",
+       "power"
+};
+static const u8 energ_perf_values[] = {
+       ENERGY_PERF_BIAS_PERFORMANCE,
+       ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
+       ENERGY_PERF_BIAS_NORMAL,
+       ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
+       ENERGY_PERF_BIAS_POWERSAVE
+};
+
+static ssize_t energy_perf_bias_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       unsigned int cpu = dev->id;
+       u64 epb;
+       int ret;
+
+       ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%llu\n", epb);
+}
+
+static ssize_t energy_perf_bias_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       unsigned int cpu = dev->id;
+       u64 epb, val;
+       int ret;
+
+       ret = __sysfs_match_string(energy_perf_strings,
+                                  ARRAY_SIZE(energy_perf_strings), buf);
+       if (ret >= 0)
+               val = energ_perf_values[ret];
+       else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
+               return -EINVAL;
+
+       ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
+       if (ret < 0)
+               return ret;
+
+       ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
+                           (epb & ~EPB_MASK) | val);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(energy_perf_bias);
+
+static struct attribute *intel_epb_attrs[] = {
+       &dev_attr_energy_perf_bias.attr,
+       NULL
+};
+
+static const struct attribute_group intel_epb_attr_group = {
+       .name = power_group_name,
+       .attrs =  intel_epb_attrs
+};
+
 static int intel_epb_online(unsigned int cpu)
 {
+       struct device *cpu_dev = get_cpu_device(cpu);
+
        intel_epb_restore();
+       if (!cpuhp_tasks_frozen)
+               sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);
+
        return 0;
 }
 
 static int intel_epb_offline(unsigned int cpu)
 {
-       return intel_epb_save();
+       struct device *cpu_dev = get_cpu_device(cpu);
+
+       if (!cpuhp_tasks_frozen)
+               sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);
+
+       intel_epb_save();
+       return 0;
 }
 
 static __init int intel_epb_init(void)