diff --git a/xen/arch/x86/acpi/cpufreq/hwp.c b/xen/arch/x86/acpi/cpufreq/hwp.c index 92fa01b8ea..620149243d 100644 --- a/xen/arch/x86/acpi/cpufreq/hwp.c +++ b/xen/arch/x86/acpi/cpufreq/hwp.c @@ -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; diff --git a/xen/drivers/acpi/pmstat.c b/xen/drivers/acpi/pmstat.c index e4e2df94a7..85097d463c 100644 --- a/xen/drivers/acpi/pmstat.c +++ b/xen/drivers/acpi/pmstat.c @@ -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); diff --git a/xen/include/acpi/cpufreq/cpufreq.h b/xen/include/acpi/cpufreq/cpufreq.h index f90e9d3a6c..281e3f513d 100644 --- a/xen/include/acpi/cpufreq/cpufreq.h +++ b/xen/include/acpi/cpufreq/cpufreq.h @@ -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__ */ diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index cf27f8f2f9..3975050248 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.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