@@ -21,6 +21,10 @@ static bool __ro_after_init feature_hdc;
static bool __ro_after_init opt_cpufreq_hdc = true;
+#define HWP_ENERGY_PERF_MAX_PERFORMANCE 0
+#define HWP_ENERGY_PERF_BALANCE 0x80
+#define HWP_ENERGY_PERF_MAX_POWERSAVE 0xff
+
union hwp_request
{
struct
@@ -542,6 +546,104 @@ int get_hwp_para(unsigned int cpu,
return 0;
}
+int set_hwp_para(struct cpufreq_policy *policy,
+ struct xen_set_cppc_para *set_cppc)
+{
+ unsigned int cpu = policy->cpu;
+ struct hwp_drv_data *data = per_cpu(hwp_drv_data, cpu);
+ bool cleared_act_window = false;
+
+ if ( data == NULL )
+ return -ENOENT;
+
+ /* Validate all parameters - Disallow reserved bits. */
+ if ( set_cppc->minimum > 255 ||
+ set_cppc->maximum > 255 ||
+ set_cppc->desired > 255 ||
+ set_cppc->energy_perf > 255 ||
+ (set_cppc->set_params & ~XEN_SYSCTL_CPPC_SET_PARAM_MASK) ||
+ (set_cppc->activity_window & ~XEN_SYSCTL_CPPC_ACT_WINDOW_MASK) )
+ return -EINVAL;
+
+ /* Only allow values if params bit is set. */
+ if ( (!(set_cppc->set_params & XEN_SYSCTL_CPPC_SET_DESIRED) &&
+ set_cppc->desired) ||
+ (!(set_cppc->set_params & XEN_SYSCTL_CPPC_SET_MINIMUM) &&
+ set_cppc->minimum) ||
+ (!(set_cppc->set_params & XEN_SYSCTL_CPPC_SET_MAXIMUM) &&
+ set_cppc->maximum) ||
+ (!(set_cppc->set_params & XEN_SYSCTL_CPPC_SET_ENERGY_PERF) &&
+ set_cppc->energy_perf) ||
+ (!(set_cppc->set_params & XEN_SYSCTL_CPPC_SET_ACT_WINDOW) &&
+ set_cppc->activity_window) )
+ return -EINVAL;
+
+ /* Clear out activity window if lacking HW supported. */
+ if ( (set_cppc->set_params & XEN_SYSCTL_CPPC_SET_ACT_WINDOW) &&
+ !feature_hwp_activity_window )
+ {
+ set_cppc->set_params &= ~XEN_SYSCTL_CPPC_SET_ACT_WINDOW;
+ cleared_act_window = true;
+ }
+
+ /* Return if there is nothing to do. */
+ if ( set_cppc->set_params == 0 )
+ return 0;
+
+ /* Apply presets */
+ switch ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_PRESET_MASK )
+ {
+ case XEN_SYSCTL_CPPC_SET_PRESET_POWERSAVE:
+ data->minimum = data->hw.lowest;
+ data->maximum = data->hw.lowest;
+ data->activity_window = 0;
+ data->energy_perf = HWP_ENERGY_PERF_MAX_POWERSAVE;
+ data->desired = 0;
+ break;
+
+ case XEN_SYSCTL_CPPC_SET_PRESET_PERFORMANCE:
+ data->minimum = data->hw.highest;
+ data->maximum = data->hw.highest;
+ data->activity_window = 0;
+ data->energy_perf = HWP_ENERGY_PERF_MAX_PERFORMANCE;
+ data->desired = 0;
+ break;
+
+ case XEN_SYSCTL_CPPC_SET_PRESET_BALANCE:
+ data->minimum = data->hw.lowest;
+ data->maximum = data->hw.highest;
+ data->activity_window = 0;
+ data->energy_perf = HWP_ENERGY_PERF_BALANCE;
+ data->desired = 0;
+ break;
+
+ case XEN_SYSCTL_CPPC_SET_PRESET_NONE:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Further customize presets if needed */
+ if ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_MINIMUM )
+ data->minimum = set_cppc->minimum;
+
+ if ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_MAXIMUM )
+ data->maximum = set_cppc->maximum;
+
+ if ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_ENERGY_PERF )
+ data->energy_perf = set_cppc->energy_perf;
+
+ if ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_DESIRED )
+ data->desired = set_cppc->desired;
+
+ if ( set_cppc->set_params & XEN_SYSCTL_CPPC_SET_ACT_WINDOW )
+ data->activity_window = set_cppc->activity_window &
+ XEN_SYSCTL_CPPC_ACT_WINDOW_MASK;
+
+ return hwp_cpufreq_target(policy, 0, 0);
+}
+
int __init hwp_register_driver(void)
{
int ret;
@@ -400,6 +400,19 @@ static int set_cpufreq_para(struct xen_sysctl_pm_op *op)
return ret;
}
+static int set_cpufreq_cppc(struct xen_sysctl_pm_op *op)
+{
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
+
+ if ( !policy || !policy->governor )
+ return -ENOENT;
+
+ if ( !hwp_active() )
+ return -EOPNOTSUPP;
+
+ return set_hwp_para(policy, &op->u.set_cppc);
+}
+
int do_pm_op(struct xen_sysctl_pm_op *op)
{
int ret = 0;
@@ -472,6 +485,10 @@ int do_pm_op(struct xen_sysctl_pm_op *op)
break;
}
+ case SET_CPUFREQ_CPPC:
+ ret = set_cpufreq_cppc(op);
+ break;
+
case GET_CPUFREQ_AVGFREQ:
{
op->u.get_avgfreq = cpufreq_driver_getavg(op->cpuid, USR_GETAVG);
@@ -257,5 +257,7 @@ int hwp_register_driver(void);
bool hwp_active(void);
int get_hwp_para(unsigned int cpu,
struct xen_cppc_para *cppc_para);
+int set_hwp_para(struct cpufreq_policy *policy,
+ struct xen_set_cppc_para *set_cppc);
#endif /* __XEN_CPUFREQ_PM_H__ */
@@ -351,6 +351,68 @@ struct xen_cppc_para {
uint32_t activity_window;
};
+/*
+ * Set CPPC values.
+ *
+ * Configure the parameters for CPPC. Set bits in set_params control which
+ * values are applied. If a bit is not set in set_params, the field must be
+ * zero.
+ *
+ * For HWP specifically, values must be limited to 0-255 or within
+ * XEN_SYSCTL_CPPC_ACT_WINDOW_MASK for activity window. Set bits outside the
+ * range will be returned as -EINVAL.
+ *
+ * Activity Window may not be supported by the hardware. In that case, the
+ * returned set_params will clear XEN_SYSCTL_CPPC_SET_ACT_WINDOW to indicate
+ * that it was not applied - though the rest of the values will be applied.
+ *
+ * There are a set of presets along with individual fields. Presets are
+ * applied first, and then individual fields. This allows customizing
+ * a preset without having to specify every value.
+ *
+ * The preset options values are as follows:
+ *
+ * preset | minimum | maxium | energy_perf
+ * ------------+---------+---------+----------------
+ * powersave | lowest | lowest | powersave (255)
+ * ------------+---------+---------+----------------
+ * balance | lowest | highest | balance (128)
+ * ------------+---------+---------+----------------
+ * performance | highest | highest | performance (0)
+ *
+ * desired and activity_window are set to 0, hardware selected.
+ */
+struct xen_set_cppc_para {
+#define XEN_SYSCTL_CPPC_SET_MINIMUM (1U << 0)
+#define XEN_SYSCTL_CPPC_SET_MAXIMUM (1U << 1)
+#define XEN_SYSCTL_CPPC_SET_DESIRED (1U << 2)
+#define XEN_SYSCTL_CPPC_SET_ENERGY_PERF (1U << 3)
+#define XEN_SYSCTL_CPPC_SET_ACT_WINDOW (1U << 4)
+#define XEN_SYSCTL_CPPC_SET_PRESET_MASK 0xf0000000U
+#define XEN_SYSCTL_CPPC_SET_PRESET_NONE 0x00000000U
+#define XEN_SYSCTL_CPPC_SET_PRESET_BALANCE 0x10000000U
+#define XEN_SYSCTL_CPPC_SET_PRESET_POWERSAVE 0x20000000U
+#define XEN_SYSCTL_CPPC_SET_PRESET_PERFORMANCE 0x30000000U
+#define XEN_SYSCTL_CPPC_SET_PARAM_MASK \
+ (XEN_SYSCTL_CPPC_SET_PRESET_MASK | \
+ XEN_SYSCTL_CPPC_SET_MINIMUM | \
+ XEN_SYSCTL_CPPC_SET_MAXIMUM | \
+ XEN_SYSCTL_CPPC_SET_DESIRED | \
+ XEN_SYSCTL_CPPC_SET_ENERGY_PERF | \
+ XEN_SYSCTL_CPPC_SET_ACT_WINDOW )
+ /* IN/OUT */
+ uint32_t set_params; /* bitflags for valid values */
+ /* See comments in struct xen_cppc_para. */
+ /* IN */
+ uint32_t minimum;
+ uint32_t maximum;
+ uint32_t desired;
+ uint32_t energy_perf;
+#define XEN_SYSCTL_CPPC_ACT_WINDOW_MASK (XEN_CPPC_ACT_WINDOW_MANTISSA_MASK | \
+ XEN_CPPC_ACT_WINDOW_EXPONENT_MASK)
+ uint32_t activity_window;
+};
+
#define XEN_HWP_DRIVER_NAME "hwp"
/*
@@ -418,6 +480,7 @@ struct xen_sysctl_pm_op {
#define SET_CPUFREQ_GOV (CPUFREQ_PARA | 0x02)
#define SET_CPUFREQ_PARA (CPUFREQ_PARA | 0x03)
#define GET_CPUFREQ_AVGFREQ (CPUFREQ_PARA | 0x04)
+ #define SET_CPUFREQ_CPPC (CPUFREQ_PARA | 0x05)
/* set/reset scheduler power saving option */
#define XEN_SYSCTL_pm_op_set_sched_opt_smt 0x21
@@ -444,6 +507,7 @@ struct xen_sysctl_pm_op {
struct xen_get_cpufreq_para get_para;
struct xen_set_cpufreq_gov set_gov;
struct xen_set_cpufreq_para set_para;
+ struct xen_set_cppc_para set_cppc;
uint64_aligned_t get_avgfreq;
uint32_t set_sched_opt_smt;
#define XEN_SYSCTL_CX_UNLIMITED 0xffffffffU