diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 6f1f70696000a..e848ea79745e1 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -327,50 +327,22 @@ Description: Energy performance preference This file is only present if the cppc-cpufreq driver is in use. -What: /sys/devices/system/cpu/cpuX/cpufreq/min_perf -Date: December 2025 +What: /sys/devices/system/cpu/cpuX/cpufreq/perf_limited +Date: February 2026 Contact: linux-pm@vger.kernel.org -Description: Minimum Performance Frequency - - Read/write a frequency value in kHz from/to this file. This - file conveys the minimum performance level (as frequency) at - which the platform may run. The frequency value is internally - converted to a performance value and must correspond to a - performance level in the range [Lowest Performance, Highest - Performance], inclusive. The minimum must be less than or equal - to the maximum performance. The performance range can be checked - from nodes: - /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf - /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf +Description: Performance Limited - This file is only present if the cppc-cpufreq driver is in use. + Read to check if platform throttling (thermal/power/current + limits) caused delivered performance to fall below the + requested level. A non-zero value indicates throttling occurred. -What: /sys/devices/system/cpu/cpuX/cpufreq/max_perf -Date: December 2025 -Contact: linux-pm@vger.kernel.org -Description: Maximum Performance Frequency - - Read/write a frequency value in kHz from/to this file. This - file conveys the maximum performance level (as frequency) at - which the platform may run. The frequency value is internally - converted to a performance value and must correspond to a - performance level in the range [Lowest Performance, Highest - Performance], inclusive. The performance range can be checked - from nodes: - /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf - /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf + Write the bitmask of bits to clear: - This file is only present if the cppc-cpufreq driver is in use. - -What: /sys/devices/system/cpu/cpuX/cpufreq/perf_limited -Date: December 2025 -Contact: linux-pm@vger.kernel.org -Description: Performance Limited + - 0x1 = clear bit 0 (desired performance excursion) + - 0x2 = clear bit 1 (minimum performance excursion) + - 0x3 = clear both bits - Read/write a 32 bits value from/to this file. This file indicates - to OSPM that an unpredictable event has limited processor - performance, and the delivered performance may be less than - desired/minimum performance. + The platform sets these bits; OSPM can only clear them. This file is only present if the cppc-cpufreq driver is in use. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 6cd934ae385d6..78aeb8be98f8c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -911,18 +911,6 @@ Format: ,,,[,] - cppc_cpufreq.auto_sel_mode= - [CPU_FREQ] Enable ACPI CPPC autonomous performance selection. - When enabled, hardware automatically adjusts CPU frequency - on all CPUs based on workload demands. In Autonomous mode, - Energy Performance Preference(EPP) hints guide hardware - toward performance(0x0) or energy efficiency (0xff). - Requires ACPI CPPC autonomous selection register support. - Format: - Default: 0 (disabled) - 0: use cpufreq governors - 1: enable if supoorted by hardware - cpuidle.off=1 [CPU_IDLE] disable the cpuidle sub-system @@ -937,6 +925,19 @@ policy to use. This governor must be registered in the kernel before the cpufreq driver probes. + cppc_cpufreq.auto_sel_mode= + [CPU_FREQ] Enable ACPI CPPC autonomous performance + selection. When enabled, hardware automatically adjusts + CPU frequency on all CPUs based on workload demands. + In Autonomous mode, Energy Performance Preference (EPP) + hints guide hardware toward performance (0x0) or energy + efficiency (0xff). + Requires ACPI CPPC autonomous selection register support. + Format: + Default: 0 (disabled) + 0: use cpufreq governors + 1: enable if supported by hardware + cpu_init_udelay=N [X86,EARLY] Delay for N microsec between assert and de-assert of APIC INIT to start processors. This delay occurs diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 40f82fefe71aa..4a89f5fc63958 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -854,6 +854,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) } per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id; + /* + * In CPPC v1, DESIRED_PERF is mandatory. In CPPC v2, it is optional + * only when AUTO_SEL_ENABLE is supported. + */ + if (!CPC_SUPPORTED(&cpc_ptr->cpc_regs[DESIRED_PERF]) && + (!osc_sb_cppc2_support_acked || + !CPC_SUPPORTED(&cpc_ptr->cpc_regs[AUTO_SEL_ENABLE]))) + pr_warn("Desired perf. register is mandatory if CPPC v2 is not supported " + "or autonomous selection is disabled\n"); + /* * Initialize the remaining cpc_regs as unsupported. * Example: In case FW exposes CPPC v2, the below loop will initialize @@ -1344,8 +1354,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_register_resource *highest_reg, *lowest_reg, *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg, - *low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL; - u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0; + *low_freq_reg = NULL, *nom_freq_reg = NULL; + u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0; int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); struct cppc_pcc_data *pcc_ss_data = NULL; int ret = 0, regs_in_pcc = 0; @@ -1362,13 +1372,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ]; nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ]; guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF]; - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; /* Are any of the regs PCC ?*/ if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) || CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) || CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || - CPC_IN_PCC(guaranteed_reg) || CPC_IN_PCC(auto_sel_reg)) { + CPC_IN_PCC(guaranteed_reg)) { if (pcc_ss_id < 0) { pr_debug("Invalid pcc_ss_id\n"); return -ENODEV; @@ -1383,42 +1392,57 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) } } - cpc_read(cpunum, highest_reg, &high); + ret = cpc_read(cpunum, highest_reg, &high); + if (ret) + goto out_err; perf_caps->highest_perf = high; - cpc_read(cpunum, lowest_reg, &low); + ret = cpc_read(cpunum, lowest_reg, &low); + if (ret) + goto out_err; perf_caps->lowest_perf = low; - cpc_read(cpunum, nominal_reg, &nom); + ret = cpc_read(cpunum, nominal_reg, &nom); + if (ret) + goto out_err; perf_caps->nominal_perf = nom; if (guaranteed_reg->type != ACPI_TYPE_BUFFER || IS_NULL_REG(&guaranteed_reg->cpc_entry.reg)) { perf_caps->guaranteed_perf = 0; } else { - cpc_read(cpunum, guaranteed_reg, &guaranteed); + ret = cpc_read(cpunum, guaranteed_reg, &guaranteed); + if (ret) + goto out_err; perf_caps->guaranteed_perf = guaranteed; } - cpc_read(cpunum, lowest_non_linear_reg, &min_nonlinear); + ret = cpc_read(cpunum, lowest_non_linear_reg, &min_nonlinear); + if (ret) + goto out_err; perf_caps->lowest_nonlinear_perf = min_nonlinear; - if (!high || !low || !nom || !min_nonlinear) + if (!high || !low || !nom || !min_nonlinear) { ret = -EFAULT; + goto out_err; + } /* Read optional lowest and nominal frequencies if present */ - if (CPC_SUPPORTED(low_freq_reg)) - cpc_read(cpunum, low_freq_reg, &low_f); + if (CPC_SUPPORTED(low_freq_reg)) { + ret = cpc_read(cpunum, low_freq_reg, &low_f); + if (ret) + goto out_err; + } - if (CPC_SUPPORTED(nom_freq_reg)) - cpc_read(cpunum, nom_freq_reg, &nom_f); + if (CPC_SUPPORTED(nom_freq_reg)) { + ret = cpc_read(cpunum, nom_freq_reg, &nom_f); + if (ret) + goto out_err; + } perf_caps->lowest_freq = low_f; perf_caps->nominal_freq = nom_f; - if (CPC_SUPPORTED(auto_sel_reg)) - cpc_read(cpunum, auto_sel_reg, &auto_sel); - perf_caps->auto_sel = (bool)auto_sel; out_err: if (regs_in_pcc) @@ -1520,9 +1544,17 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) } } - cpc_read(cpunum, delivered_reg, &delivered); - cpc_read(cpunum, reference_reg, &reference); - cpc_read(cpunum, ref_perf_reg, &ref_perf); + ret = cpc_read(cpunum, delivered_reg, &delivered); + if (ret) + goto out_err; + + ret = cpc_read(cpunum, reference_reg, &reference); + if (ret) + goto out_err; + + ret = cpc_read(cpunum, ref_perf_reg, &ref_perf); + if (ret) + goto out_err; /* * Per spec, if ctr_wrap_time optional register is unsupported, then the @@ -1530,8 +1562,11 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) * platform */ ctr_wrap_time = (u64)(~((u64)0)); - if (CPC_SUPPORTED(ctr_wrap_reg)) - cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time); + if (CPC_SUPPORTED(ctr_wrap_reg)) { + ret = cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time); + if (ret) + goto out_err; + } if (!delivered || !reference || !ref_perf) { ret = -EFAULT; @@ -1560,8 +1595,8 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable) struct cpc_register_resource *auto_sel_reg; struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; - bool autosel_support_in_ffh_or_sysmem; - bool epp_support_in_ffh_or_sysmem; + bool autosel_ffh_sysmem; + bool epp_ffh_sysmem; int ret; if (!cpc_desc) { @@ -1572,10 +1607,10 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable) auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF]; - epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) && - (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg)); - autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) && - (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg)); + epp_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) && + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg)); + autosel_ffh_sysmem = CPC_SUPPORTED(auto_sel_reg) && + (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg)); if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) { if (pcc_ss_id < 0) { @@ -1601,29 +1636,25 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable) /* after writing CPC, transfer the ownership of PCC to platform */ ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE); up_write(&pcc_ss_data->pcc_lock); - } else if (osc_cpc_flexible_adr_space_confirmed) { - if (!epp_support_in_ffh_or_sysmem && !autosel_support_in_ffh_or_sysmem) { - ret = -EOPNOTSUPP; - } else { - if (autosel_support_in_ffh_or_sysmem) { - ret = cpc_write(cpu, auto_sel_reg, enable); - if (ret) - return ret; - } + } else if (osc_cpc_flexible_adr_space_confirmed && + (epp_ffh_sysmem || autosel_ffh_sysmem)) { + if (autosel_ffh_sysmem) { + ret = cpc_write(cpu, auto_sel_reg, enable); + if (ret) + return ret; + } - if (epp_support_in_ffh_or_sysmem) { - ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf); - if (ret) - return ret; - } + if (epp_ffh_sysmem) { + ret = cpc_write(cpu, epp_set_reg, + perf_ctrls->energy_perf); + if (ret) + return ret; } } else { - ret = -EOPNOTSUPP; + ret = -ENOTSUPP; + pr_debug("_CPC in PCC/FFH/SystemMemory are not supported\n"); } - if (ret == -EOPNOTSUPP) - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n"); - return ret; } EXPORT_SYMBOL_GPL(cppc_set_epp_perf); @@ -1759,84 +1790,6 @@ int cppc_set_enable(int cpu, bool enable) } EXPORT_SYMBOL_GPL(cppc_set_enable); -/** - * cppc_get_min_perf - Get the min performance register value. - * @cpu: CPU from which to get min performance. - * @min_perf: Return address. - * - * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported. - */ -int cppc_get_min_perf(int cpu, u64 *min_perf) -{ - return cppc_get_reg_val(cpu, MIN_PERF, min_perf); -} -EXPORT_SYMBOL_GPL(cppc_get_min_perf); - -/** - * cppc_set_min_perf() - Write the min performance register. - * @cpu: CPU on which to write register. - * @min_perf: Value to write to the MIN_PERF register. - * - * Return: 0 for success, -EIO otherwise. - */ -int cppc_set_min_perf(int cpu, u64 min_perf) -{ - return cppc_set_reg_val(cpu, MIN_PERF, min_perf); -} -EXPORT_SYMBOL_GPL(cppc_set_min_perf); - -/** - * cppc_get_max_perf - Get the max performance register value. - * @cpu: CPU from which to get max performance. - * @max_perf: Return address. - * - * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported. - */ -int cppc_get_max_perf(int cpu, u64 *max_perf) -{ - return cppc_get_reg_val(cpu, MAX_PERF, max_perf); -} -EXPORT_SYMBOL_GPL(cppc_get_max_perf); - -/** - * cppc_set_max_perf() - Write the max performance register. - * @cpu: CPU on which to write register. - * @max_perf: Value to write to the MAX_PERF register. - * - * Return: 0 for success, -EIO otherwise. - */ -int cppc_set_max_perf(int cpu, u64 max_perf) -{ - return cppc_set_reg_val(cpu, MAX_PERF, max_perf); -} -EXPORT_SYMBOL_GPL(cppc_set_max_perf); - -/** - * cppc_get_perf_limited - Get the Performance Limited register value. - * @cpu: CPU from which to get Performance Limited register. - * @perf_limited: Pointer to store the Performance Limited value. - * - * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported. - */ -int cppc_get_perf_limited(int cpu, u64 *perf_limited) -{ - return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited); -} -EXPORT_SYMBOL_GPL(cppc_get_perf_limited); - -/** - * cppc_set_perf_limited() - Write the Performance Limited register. - * @cpu: CPU on which to write register. - * @perf_limited: Value to write to the perf_limited register. - * - * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported. - */ -int cppc_set_perf_limited(int cpu, u64 perf_limited) -{ - return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited); -} -EXPORT_SYMBOL_GPL(cppc_set_perf_limited); - /** * cppc_get_perf - Get a CPU's performance controls. * @cpu: CPU for which to get performance controls. @@ -1847,9 +1800,10 @@ EXPORT_SYMBOL_GPL(cppc_set_perf_limited); int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) { struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cpc_register_resource *desired_perf_reg, *min_perf_reg, *max_perf_reg, - *energy_perf_reg; - u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0; + struct cpc_register_resource *desired_perf_reg, + *min_perf_reg, *max_perf_reg, + *energy_perf_reg, *auto_sel_reg; + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 0; int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; int ret = 0, regs_in_pcc = 0; @@ -1868,10 +1822,12 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF]; max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF]; energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF]; + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; /* Are any of the regs PCC ?*/ if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) || - CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) { + CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg) || + CPC_IN_PCC(auto_sel_reg)) { if (pcc_ss_id < 0) { pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu); return -ENODEV; @@ -1881,29 +1837,47 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) down_write(&pcc_ss_data->pcc_lock); /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) { - pr_debug("Failed to send PCC command for CPU:%d, ret:%d\n", cpu, ret); ret = -EIO; goto out_err; } } /* Read optional elements if present */ - if (CPC_SUPPORTED(max_perf_reg)) - cpc_read(cpu, max_perf_reg, &max); + if (CPC_SUPPORTED(max_perf_reg)) { + ret = cpc_read(cpu, max_perf_reg, &max); + if (ret) + goto out_err; + } perf_ctrls->max_perf = max; - if (CPC_SUPPORTED(min_perf_reg)) - cpc_read(cpu, min_perf_reg, &min); + if (CPC_SUPPORTED(min_perf_reg)) { + ret = cpc_read(cpu, min_perf_reg, &min); + if (ret) + goto out_err; + } perf_ctrls->min_perf = min; - if (CPC_SUPPORTED(desired_perf_reg)) - cpc_read(cpu, desired_perf_reg, &desired_perf); + if (CPC_SUPPORTED(desired_perf_reg)) { + ret = cpc_read(cpu, desired_perf_reg, &desired_perf); + if (ret) + goto out_err; + } perf_ctrls->desired_perf = desired_perf; - if (CPC_SUPPORTED(energy_perf_reg)) - cpc_read(cpu, energy_perf_reg, &energy_perf); + if (CPC_SUPPORTED(energy_perf_reg)) { + ret = cpc_read(cpu, energy_perf_reg, &energy_perf); + if (ret) + goto out_err; + } perf_ctrls->energy_perf = energy_perf; + if (CPC_SUPPORTED(auto_sel_reg)) { + ret = cpc_read(cpu, auto_sel_reg, &auto_sel); + if (ret) + goto out_err; + } + perf_ctrls->auto_sel = (bool)auto_sel; + out_err: if (regs_in_pcc) up_write(&pcc_ss_data->pcc_lock); @@ -2043,6 +2017,62 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) } EXPORT_SYMBOL_GPL(cppc_set_perf); +/** + * cppc_get_perf_limited - Get the Performance Limited register value. + * @cpu: CPU from which to get Performance Limited register. + * @perf_limited: Pointer to store the Performance Limited value. + * + * The returned value contains sticky status bits indicating platform-imposed + * performance limitations. + * + * Return: 0 for success, -EIO on failure, -EOPNOTSUPP if not supported. + */ +int cppc_get_perf_limited(int cpu, u64 *perf_limited) +{ + return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited); +} +EXPORT_SYMBOL_GPL(cppc_get_perf_limited); + +/** + * cppc_set_perf_limited() - Clear bits in the Performance Limited register. + * @cpu: CPU on which to write register. + * @bits_to_clear: Bitmask of bits to clear in the perf_limited register. + * + * The Performance Limited register contains two sticky bits set by platform: + * - Bit 0 (Desired_Excursion): Set when delivered performance is constrained + * below desired performance. Not used when Autonomous Selection is enabled. + * - Bit 1 (Minimum_Excursion): Set when delivered performance is constrained + * below minimum performance. + * + * These bits are sticky and remain set until OSPM explicitly clears them. + * This function only allows clearing bits (the platform sets them). + * + * Return: 0 for success, -EINVAL for invalid bits, -EIO on register + * access failure, -EOPNOTSUPP if not supported. + */ +int cppc_set_perf_limited(int cpu, u64 bits_to_clear) +{ + u64 current_val, new_val; + int ret; + + /* Only bits 0 and 1 are valid */ + if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK) + return -EINVAL; + + if (!bits_to_clear) + return 0; + + ret = cppc_get_perf_limited(cpu, ¤t_val); + if (ret) + return ret; + + /* Clear the specified bits */ + new_val = current_val & ~bits_to_clear; + + return cppc_set_reg_val(cpu, PERF_LIMITED, new_val); +} +EXPORT_SYMBOL_GPL(cppc_set_perf_limited); + /** * cppc_get_transition_latency - returns frequency transition latency in ns * @cpu_num: CPU number for per_cpu(). diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 7be26007f1d8e..8e156d06158e1 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1003,12 +1003,12 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = perf_to_freq(perf, - cpudata->nominal_freq, - perf.highest_perf); + policy->cpuinfo.min_freq = perf_to_freq(perf, + cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = perf_to_freq(perf, + cpudata->nominal_freq, + perf.highest_perf); ret = amd_pstate_cppc_enable(policy); if (ret) @@ -1492,12 +1492,12 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = perf_to_freq(perf, - cpudata->nominal_freq, - perf.highest_perf); + policy->cpuinfo.min_freq = perf_to_freq(perf, + cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = perf_to_freq(perf, + cpudata->nominal_freq, + perf.highest_perf); policy->driver_data = cpudata; ret = amd_pstate_cppc_enable(policy); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 08d3fe50d3f79..c98aa8850d7c0 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -23,15 +23,12 @@ #include #include -#include #include static struct cpufreq_driver cppc_cpufreq_driver; -static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock); - -/* Autonomous Selection */ +/* Autonomous Selection boot parameter */ static bool auto_sel_mode; #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE @@ -264,6 +261,21 @@ static inline void cppc_freq_invariance_exit(void) } #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ +static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy) +{ + struct cppc_perf_caps *caps = &cpu_data->perf_caps; + u32 min_perf, max_perf; + + min_perf = cppc_khz_to_perf(caps, policy->min); + max_perf = cppc_khz_to_perf(caps, policy->max); + + cpu_data->perf_ctrls.min_perf = + clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf); + cpu_data->perf_ctrls.max_perf = + clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf); +} + static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) @@ -275,16 +287,13 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, cpu_data->perf_ctrls.desired_perf = cppc_khz_to_perf(&cpu_data->perf_caps, target_freq); + cppc_cpufreq_update_perf_limits(cpu_data, policy); + freqs.old = policy->cur; freqs.new = target_freq; - /* - * In autonomous selection mode, hardware handles frequency scaling directly - * based on workload and EPP hints. So, skip the OS frequency set requests. - */ cpufreq_freq_transition_begin(policy, &freqs); - if (!cpu_data->perf_caps.auto_sel) - ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); cpufreq_freq_transition_end(policy, &freqs, ret != 0); if (ret) @@ -304,8 +313,9 @@ static unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy, desired_perf = cppc_khz_to_perf(&cpu_data->perf_caps, target_freq); cpu_data->perf_ctrls.desired_perf = desired_perf; - ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); + cppc_cpufreq_update_perf_limits(cpu_data, policy); + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); if (ret) { pr_debug("Failed to set target on CPU:%d. ret:%d\n", cpu, ret); @@ -601,163 +611,12 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) policy->driver_data = NULL; } -/** - * cppc_cpufreq_set_mperf_limit - Generic function to set min/max performance limit - * @policy: cpufreq policy - * @val: performance value to set - * @update_reg: whether to update hardware register - * @update_policy: whether to update policy constraints - * @is_min: true for min_perf, false for max_perf - */ -static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val, - bool update_reg, bool update_policy, bool is_min) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - struct cppc_perf_caps *caps = &cpu_data->perf_caps; - unsigned int cpu = policy->cpu; - struct freq_qos_request *req; - unsigned int freq; - u32 perf; - int ret; - - perf = clamp(val, caps->lowest_perf, caps->highest_perf); - freq = cppc_perf_to_khz(caps, perf); - - pr_debug("cpu%d, %s_perf:%llu, update_reg:%d, update_policy:%d\n", cpu, - is_min ? "min" : "max", (u64)perf, update_reg, update_policy); - - guard(mutex)(&cppc_cpufreq_update_autosel_config_lock); - - if (update_reg) { - ret = is_min ? cppc_set_min_perf(cpu, perf) : cppc_set_max_perf(cpu, perf); - if (ret) { - if (ret != -EOPNOTSUPP) - pr_warn("Failed to set %s_perf (%llu) on CPU%d (%d)\n", - is_min ? "min" : "max", (u64)perf, cpu, ret); - return ret; - } - - if (is_min) - cpu_data->perf_ctrls.min_perf = perf; - else - cpu_data->perf_ctrls.max_perf = perf; - } - - if (update_policy) { - req = is_min ? policy->min_freq_req : policy->max_freq_req; - - ret = freq_qos_update_request(req, freq); - if (ret < 0) { - pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n", - is_min ? "min" : "max", cpu, ret); - return ret; - } - } - - return 0; -} - -#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy) \ - cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, true) - -#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \ - cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false) - -static int cppc_cpufreq_update_autosel_val(struct cpufreq_policy *policy, bool auto_sel) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - unsigned int cpu = policy->cpu; - int ret; - - pr_debug("cpu%d, auto_sel curr:%u, new:%d\n", cpu, cpu_data->perf_caps.auto_sel, auto_sel); - - guard(mutex)(&cppc_cpufreq_update_autosel_config_lock); - - ret = cppc_set_auto_sel(cpu, auto_sel); - if (ret) { - pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n", auto_sel, cpu, ret); - return ret; - } - cpu_data->perf_caps.auto_sel = auto_sel; - - return 0; -} - -static int cppc_cpufreq_update_epp_val(struct cpufreq_policy *policy, u32 epp) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - unsigned int cpu = policy->cpu; - int ret; - - pr_debug("cpu%d, epp curr:%u, new:%u\n", cpu, cpu_data->perf_ctrls.energy_perf, epp); - - guard(mutex)(&cppc_cpufreq_update_autosel_config_lock); - - ret = cppc_set_epp(cpu, epp); - if (ret) { - pr_warn("failed to set energy_perf for cpu:%d (%d)\n", cpu, ret); - return ret; - } - cpu_data->perf_ctrls.energy_perf = epp; - - return 0; -} - -/** - * cppc_cpufreq_update_autosel_config - Update Autonomous selection configuration - * @policy: cpufreq policy for the CPU - * @min_perf: minimum performance value to set - * @max_perf: maximum performance value to set - * @auto_sel: autonomous selection mode enable/disable (also controls min/max perf reg updates) - * @epp_val: energy performance preference value - * @update_epp: whether to update EPP register - * @update_policy: whether to update policy constraints - * - * Return: 0 on success, negative error code on failure - */ -static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy, - u64 min_perf, u64 max_perf, bool auto_sel, - u32 epp_val, bool update_epp, bool update_policy) -{ - const unsigned int cpu = policy->cpu; - int ret; - - /* - * Set min/max performance registers and update policy constraints. - * When enabling: update both registers and policy. - * When disabling: update policy only. - * Continue even if min/max are not supported, as EPP and autosel - * might still be supported. - */ - ret = cppc_cpufreq_set_min_perf(policy, min_perf, auto_sel, update_policy); - if (ret && ret != -EOPNOTSUPP) - return ret; - - ret = cppc_cpufreq_set_max_perf(policy, max_perf, auto_sel, update_policy); - if (ret && ret != -EOPNOTSUPP) - return ret; - - if (update_epp) { - ret = cppc_cpufreq_update_epp_val(policy, epp_val); - if (ret) - return ret; - } - - ret = cppc_cpufreq_update_autosel_val(policy, auto_sel); - if (ret) - return ret; - - pr_debug("Updated autonomous config [%llu-%llu] for CPU%d\n", min_perf, max_perf, cpu); - - return 0; -} - static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) { unsigned int cpu = policy->cpu; struct cppc_cpudata *cpu_data; struct cppc_perf_caps *caps; - u64 min_perf, max_perf; + unsigned int min, max; int ret; cpu_data = cppc_cpufreq_get_cpu_data(cpu); @@ -768,13 +627,23 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) caps = &cpu_data->perf_caps; policy->driver_data = cpu_data; + /* + * Enable CPPC for both OS-driven and autonomous modes. + * The Enable register is optional - some platforms may not support it + */ + ret = cppc_set_enable(cpu, true); + if (ret && ret != -EOPNOTSUPP) + pr_warn("Failed to enable CPPC for CPU%d (%d)\n", cpu, ret); + + min = cppc_perf_to_khz(caps, caps->lowest_nonlinear_perf); + max = cppc_perf_to_khz(caps, policy->boost_enabled ? + caps->highest_perf : caps->nominal_perf); + /* * Set min to lowest nonlinear perf to avoid any efficiency penalty (see * Section 8.4.7.1.1.5 of ACPI 6.1 spec) */ - policy->min = cppc_perf_to_khz(caps, caps->lowest_nonlinear_perf); - policy->max = cppc_perf_to_khz(caps, policy->boost_enabled ? - caps->highest_perf : caps->nominal_perf); + policy->min = min; /* * Set cpuinfo.min_freq to Lowest to make the full range of performance @@ -782,7 +651,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) * nonlinear perf */ policy->cpuinfo.min_freq = cppc_perf_to_khz(caps, caps->lowest_perf); - policy->cpuinfo.max_freq = policy->max; + policy->cpuinfo.max_freq = max; policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu); policy->shared_type = cpu_data->shared_type; @@ -821,25 +690,65 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cur = cppc_perf_to_khz(caps, caps->highest_perf); cpu_data->perf_ctrls.desired_perf = caps->highest_perf; - if (cpu_data->perf_caps.auto_sel) { - ret = cppc_set_enable(cpu, true); + /* + * Enable autonomous mode on first init if boot param is set. + * Check last_governor to detect first init and skip if auto_sel + * is already enabled. + */ + if (auto_sel_mode && policy->last_governor[0] == '\0' && + !cpu_data->perf_ctrls.auto_sel) { + /* Init min/max_perf from caps if not already set by HW. */ + if (!cpu_data->perf_ctrls.min_perf) + cpu_data->perf_ctrls.min_perf = caps->lowest_nonlinear_perf; + if (!cpu_data->perf_ctrls.max_perf) + cpu_data->perf_ctrls.max_perf = policy->boost_enabled ? + caps->highest_perf : caps->nominal_perf; + + cpu_data->perf_ctrls.desired_perf = + clamp_t(u32, cpu_data->perf_ctrls.desired_perf, + cpu_data->perf_ctrls.min_perf, + cpu_data->perf_ctrls.max_perf); + + policy->cur = cppc_perf_to_khz(caps, + cpu_data->perf_ctrls.desired_perf); + + /* EPP is optional - some platforms may not support it */ + ret = cppc_set_epp(cpu, CPPC_EPP_PERFORMANCE_PREF); + if (ret && ret != -EOPNOTSUPP) + pr_warn("Failed to set EPP for CPU%d (%d)\n", cpu, ret); + else if (!ret) + cpu_data->perf_ctrls.energy_perf = CPPC_EPP_PERFORMANCE_PREF; + + /* Program min/max/desired into CPPC regs before enabling auto_sel. */ + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); if (ret) { - pr_err("Failed to enable CPPC on cpu%d (%d)\n", cpu, ret); + pr_debug("Err setting perf for autonomous mode CPU:%d ret:%d\n", + cpu, ret); goto out; } - min_perf = cpu_data->perf_ctrls.min_perf ? - cpu_data->perf_ctrls.min_perf : caps->lowest_nonlinear_perf; - max_perf = cpu_data->perf_ctrls.max_perf ? - cpu_data->perf_ctrls.max_perf : caps->nominal_perf; - - ret = cppc_cpufreq_update_autosel_config(policy, min_perf, max_perf, true, - CPPC_EPP_PERFORMANCE_PREF, true, false); - if (ret) { - cppc_set_enable(cpu, false); + ret = cppc_set_auto_sel(cpu, true); + if (ret && ret != -EOPNOTSUPP) { + pr_warn("Failed autonomous config for CPU%d (%d)\n", + cpu, ret); goto out; } + if (!ret) + cpu_data->perf_ctrls.auto_sel = true; + } + + if (cpu_data->perf_ctrls.auto_sel) { + /* Sync policy limits from HW when autonomous mode is active */ + policy->min = cppc_perf_to_khz(caps, + cpu_data->perf_ctrls.min_perf ?: + caps->lowest_nonlinear_perf); + policy->max = cppc_perf_to_khz(caps, + cpu_data->perf_ctrls.max_perf ?: + (policy->boost_enabled ? + caps->highest_perf : + caps->nominal_perf)); } else { + /* Normal mode: governors control frequency */ ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); if (ret) { pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", @@ -1010,31 +919,10 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf) return sysfs_emit(buf, "%d\n", val); } -/** - * cppc_cpufreq_update_auto_select - Update autonomous selection config for policy->cpu - * @policy: cpufreq policy - * @enable: enable/disable autonomous selection - */ -static int cppc_cpufreq_update_auto_select(struct cpufreq_policy *policy, bool enable) +static ssize_t store_auto_select(struct cpufreq_policy *policy, + const char *buf, size_t count) { struct cppc_cpudata *cpu_data = policy->driver_data; - struct cppc_perf_caps *caps = &cpu_data->perf_caps; - u64 min_perf = caps->lowest_nonlinear_perf; - u64 max_perf = caps->nominal_perf; - - if (enable) { - if (cpu_data->perf_ctrls.min_perf) - min_perf = cpu_data->perf_ctrls.min_perf; - if (cpu_data->perf_ctrls.max_perf) - max_perf = cpu_data->perf_ctrls.max_perf; - } - - return cppc_cpufreq_update_autosel_config(policy, min_perf, max_perf, enable, - 0, false, true); -} - -static ssize_t store_auto_select(struct cpufreq_policy *policy, const char *buf, size_t count) -{ bool val; int ret; @@ -1042,27 +930,54 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy, const char *buf, if (ret) return ret; - ret = cppc_cpufreq_update_auto_select(policy, val); + ret = cppc_set_auto_sel(policy->cpu, val); if (ret) return ret; + cpu_data->perf_ctrls.auto_sel = val; + + if (val) { + u32 old_min_perf = cpu_data->perf_ctrls.min_perf; + u32 old_max_perf = cpu_data->perf_ctrls.max_perf; + + /* + * When enabling autonomous selection, program MIN_PERF and + * MAX_PERF from current policy limits so that the platform + * uses the correct performance bounds immediately. + */ + cppc_cpufreq_update_perf_limits(cpu_data, policy); + + ret = cppc_set_perf(policy->cpu, &cpu_data->perf_ctrls); + if (ret) { + cpu_data->perf_ctrls.min_perf = old_min_perf; + cpu_data->perf_ctrls.max_perf = old_max_perf; + cppc_set_auto_sel(policy->cpu, false); + cpu_data->perf_ctrls.auto_sel = false; + return ret; + } + } + return count; } -static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu, int (*get_func)(int, u64 *), char *buf) +static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu, + int (*get_func)(int, u64 *), + char *buf) { u64 val; - int ret = get_func(cpu, &val); + int ret = get_func((int)cpu, &val); if (ret == -EOPNOTSUPP) return sysfs_emit(buf, "\n"); + if (ret) return ret; return sysfs_emit(buf, "%llu\n", val); } -static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, int (*set_func)(int, u64), +static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, + int (*set_func)(int, u64), const char *buf, size_t count) { u64 val; @@ -1077,141 +992,55 @@ static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, int (*set_func)(in return ret ? ret : count; } -static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf) -{ - return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_auto_act_window, buf); +#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \ +static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \ +{ \ + return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\ +} \ +static ssize_t store_##_name(struct cpufreq_policy *policy, \ + const char *buf, size_t count) \ +{ \ + return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \ + buf, count); \ } -static ssize_t store_auto_act_window(struct cpufreq_policy *policy, const char *buf, size_t count) -{ - return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_auto_act_window, buf, count); -} +CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window, + cppc_set_auto_act_window) -static ssize_t show_energy_performance_preference_val(struct cpufreq_policy *policy, char *buf) +static ssize_t +show_energy_performance_preference_val(struct cpufreq_policy *policy, char *buf) { return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_epp_perf, buf); } -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy, - const char *buf, size_t count) -{ - return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp, buf, count); -} - -/** - * show_min_perf - Show minimum performance as frequency (kHz) - * - * Reads the MIN_PERF register and converts the performance value to - * frequency (kHz) for user-space consumption. - */ -static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - u64 perf; - int ret; - - ret = cppc_get_min_perf(policy->cpu, &perf); - if (ret == -EOPNOTSUPP) - return sysfs_emit(buf, "\n"); - if (ret) - return ret; - - /* Convert performance to frequency (kHz) for user */ - return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf)); -} - -/** - * store_min_perf - Set minimum performance from frequency (kHz) - * - * Converts the user-provided frequency (kHz) to a performance value - * and writes it to the MIN_PERF register. - */ -static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf, size_t count) +static ssize_t +store_energy_performance_preference_val(struct cpufreq_policy *policy, + const char *buf, size_t count) { struct cppc_cpudata *cpu_data = policy->driver_data; - unsigned int freq_khz; - u64 perf; - int ret; - - ret = kstrtouint(buf, 0, &freq_khz); - if (ret) - return ret; - - /* Convert frequency (kHz) to performance value */ - perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz); - - ret = cppc_cpufreq_set_min_perf(policy, perf, true, cpu_data->perf_caps.auto_sel); - if (ret) - return ret; - - return count; -} - -/** - * show_max_perf - Show maximum performance as frequency (kHz) - * - * Reads the MAX_PERF register and converts the performance value to - * frequency (kHz) for user-space consumption. - */ -static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - u64 perf; + u64 val; int ret; - ret = cppc_get_max_perf(policy->cpu, &perf); - if (ret == -EOPNOTSUPP) - return sysfs_emit(buf, "\n"); + ret = kstrtou64(buf, 0, &val); if (ret) return ret; - /* Convert performance to frequency (kHz) for user */ - return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf)); -} - -/** - * store_max_perf - Set maximum performance from frequency (kHz) - * - * Converts the user-provided frequency (kHz) to a performance value - * and writes it to the MAX_PERF register. - */ -static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count) -{ - struct cppc_cpudata *cpu_data = policy->driver_data; - unsigned int freq_khz; - u64 perf; - int ret; - - ret = kstrtouint(buf, 0, &freq_khz); + ret = cppc_set_epp(policy->cpu, val); if (ret) return ret; - /* Convert frequency (kHz) to performance value */ - perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz); - - ret = cppc_cpufreq_set_max_perf(policy, perf, true, cpu_data->perf_caps.auto_sel); - if (ret) - return ret; + cpu_data->perf_ctrls.energy_perf = val; return count; } -static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf) -{ - return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf); -} - -static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count) -{ - return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, count); -} +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited, + cppc_set_perf_limited) cpufreq_freq_attr_ro(freqdomain_cpus); cpufreq_freq_attr_rw(auto_select); cpufreq_freq_attr_rw(auto_act_window); cpufreq_freq_attr_rw(energy_performance_preference_val); -cpufreq_freq_attr_rw(min_perf); -cpufreq_freq_attr_rw(max_perf); cpufreq_freq_attr_rw(perf_limited); static struct freq_attr *cppc_cpufreq_attr[] = { @@ -1219,8 +1048,6 @@ static struct freq_attr *cppc_cpufreq_attr[] = { &auto_select, &auto_act_window, &energy_performance_preference_val, - &min_perf, - &max_perf, &perf_limited, NULL, }; @@ -1238,61 +1065,13 @@ static struct cpufreq_driver cppc_cpufreq_driver = { .name = "cppc_cpufreq", }; -static int cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64 epp) -{ - int cpu, ret; - - for_each_present_cpu(cpu) { - ret = cppc_set_epp(cpu, epp); - if (ret) { - pr_warn("Failed to set EPP on CPU%d (%d)\n", cpu, ret); - goto disable_all; - } - - ret = cppc_set_auto_sel(cpu, auto_sel); - if (ret) { - pr_warn("Failed to set auto_sel on CPU%d (%d)\n", cpu, ret); - goto disable_all; - } - } - - return 0; - -disable_all: - pr_warn("Disabling auto_sel for all CPUs\n"); - for_each_present_cpu(cpu) - cppc_set_auto_sel(cpu, false); - - return -EIO; -} - static int __init cppc_cpufreq_init(void) { - bool auto_sel; int ret; if (!acpi_cpc_valid()) return -ENODEV; - if (auto_sel_mode) { - /* - * Check if autonomous selection is supported by testing CPU 0. - * If supported, enable autonomous mode on all CPUs. - */ - ret = cppc_get_auto_sel(0, &auto_sel); - if (!ret) { - pr_info("Enabling auto_sel_mode (autonomous selection mode)\n"); - ret = cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF); - if (ret) { - pr_warn("Disabling auto_sel_mode, fallback to standard\n"); - auto_sel_mode = false; - } - } else { - pr_warn("Disabling auto_sel_mode as not supported by hardware\n"); - auto_sel_mode = false; - } - } - cppc_freq_invariance_init(); populate_efficiency_class(); @@ -1305,18 +1084,17 @@ static int __init cppc_cpufreq_init(void) static void __exit cppc_cpufreq_exit(void) { - int cpu; + unsigned int cpu; for_each_present_cpu(cpu) cppc_set_auto_sel(cpu, false); - auto_sel_mode = false; cpufreq_unregister_driver(&cppc_cpufreq_driver); cppc_freq_invariance_exit(); } -module_param(auto_sel_mode, bool, 0000); -MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection"); +module_param(auto_sel_mode, bool, 0444); +MODULE_PARM_DESC(auto_sel_mode, "Enable CPPC autonomous performance selection at boot"); module_exit(cppc_cpufreq_exit); MODULE_AUTHOR("Ashwin Chaugule"); diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index fbbbe501cf2dc..831102522ad64 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -355,8 +355,8 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) min_fsb = NFORCE2_MIN_FSB; /* cpuinfo and default policy values */ - policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; - policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; + policy->cpuinfo.min_freq = min_fsb * fid * 100; + policy->cpuinfo.max_freq = max_fsb * fid * 100; return 0; } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 58e3839a21400..fbd20d7ec41cc 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1435,6 +1435,14 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); if (new_policy) { + unsigned int min, max; + + /* Use policy->min/max set by the driver as QoS requests. */ + min = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min); + if (policy->max) + max = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max); + else + max = FREQ_QOS_MAX_DEFAULT_VALUE; for_each_cpu(j, policy->related_cpus) { per_cpu(cpufreq_cpu_data, j) = policy; add_cpu_dev_symlink(policy, j, get_cpu_device(j)); @@ -1449,7 +1457,7 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, ret = freq_qos_add_request(&policy->constraints, policy->min_freq_req, FREQ_QOS_MIN, - FREQ_QOS_MIN_DEFAULT_VALUE); + min); if (ret < 0) { /* * So we don't call freq_qos_remove_request() for an @@ -1469,7 +1477,7 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, ret = freq_qos_add_request(&policy->constraints, policy->max_freq_req, FREQ_QOS_MAX, - FREQ_QOS_MAX_DEFAULT_VALUE); + max); if (ret < 0) { policy->max_freq_req = NULL; goto out_destroy_policy; @@ -1477,10 +1485,13 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); - } else { - ret = freq_qos_update_request(policy->max_freq_req, policy->max); - if (ret < 0) - goto out_destroy_policy; + + /* + * If the driver didn't set QoS constraints, policy->min/max still + * need to be set as they are used to clamp frequency requests. + */ + policy->min = policy->min ? policy->min : policy->cpuinfo.min_freq; + policy->max = policy->max ? policy->max : policy->cpuinfo.max_freq; } if (cpufreq_driver->get && has_target()) { diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 35de513af6c94..a4b1bf7da203c 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -50,16 +50,15 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, max_freq = freq; } - policy->min = policy->cpuinfo.min_freq = min_freq; - policy->max = max_freq; + policy->cpuinfo.min_freq = min_freq; /* * If the driver has set its own cpuinfo.max_freq above max_freq, leave * it as is. */ if (policy->cpuinfo.max_freq < max_freq) - policy->max = policy->cpuinfo.max_freq = max_freq; + policy->cpuinfo.max_freq = max_freq; - if (policy->min == ~0) + if (min_freq == ~0) return -EINVAL; else return 0; diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 75b3ef7ec6796..10ee3094345c3 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -397,7 +397,7 @@ static int cpufreq_gx_target(struct cpufreq_policy *policy, static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) { - unsigned int maxfreq; + unsigned int minfreq, maxfreq; if (!policy || policy->cpu != 0) return -ENODEV; @@ -418,10 +418,11 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) policy->cpu = 0; if (max_duration < POLICY_MIN_DIV) - policy->min = maxfreq / max_duration; + minfreq = maxfreq / max_duration; else - policy->min = maxfreq / POLICY_MIN_DIV; - policy->max = maxfreq; + minfreq = maxfreq / POLICY_MIN_DIV; + + policy->min = minfreq; policy->cpuinfo.min_freq = maxfreq / max_duration; policy->cpuinfo.max_freq = maxfreq; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 99c80249fde88..40ec473afc790 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3113,9 +3113,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - intel_pstate_init_acpi_perf_limits(policy); policy->fast_switch_possible = true; diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index ac2e90a65f0c4..231edfe8cabaa 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -551,13 +551,13 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->max = policy->cpuinfo.max_freq = + policy->cpuinfo.max_freq = ioread32(&pcch_hdr->nominal) * 1000; - policy->min = policy->cpuinfo.min_freq = + policy->cpuinfo.min_freq = ioread32(&pcch_hdr->minimum_frequency) * 1000; - pr_debug("init: policy->max is %d, policy->min is %d\n", - policy->max, policy->min); + pr_debug("init: max_freq is %d, min_freq is %d\n", + policy->cpuinfo.max_freq, policy->cpuinfo.min_freq); out: return result; } diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 4afa48d172dbe..f53b9d7edc76a 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -185,8 +185,8 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy) int ret = -EINVAL; /* set default policy and cpuinfo */ - policy->min = policy->cpuinfo.min_freq = 104000; - policy->max = policy->cpuinfo.max_freq = + policy->cpuinfo.min_freq = 104000; + policy->cpuinfo.max_freq = (cpu_is_pxa320()) ? 806000 : 624000; policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 9c0b01e00508c..f917128566952 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -126,9 +126,9 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) dev_notice(dev, "no frequency table found, falling back " "to rate rounding.\n"); - policy->min = policy->cpuinfo.min_freq = + policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; - policy->max = policy->cpuinfo.max_freq = + policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } diff --git a/drivers/cpufreq/virtual-cpufreq.c b/drivers/cpufreq/virtual-cpufreq.c index 7dd1b0c263c7e..6d78afb9fd13e 100644 --- a/drivers/cpufreq/virtual-cpufreq.c +++ b/drivers/cpufreq/virtual-cpufreq.c @@ -164,10 +164,7 @@ static int virt_cpufreq_get_freq_info(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = 1; policy->cpuinfo.max_freq = virt_cpufreq_get_perftbl_entry(policy->cpu, 0); - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - - policy->cur = policy->max; + policy->cur = policy->cpuinfo.max_freq; return 0; } diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 9fc28fb1890be..0cd6832e1911e 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -42,6 +42,11 @@ #define CPPC_EPP_PERFORMANCE_PREF 0x00 #define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF +#define CPPC_PERF_LIMITED_DESIRED_EXCURSION BIT(0) +#define CPPC_PERF_LIMITED_MINIMUM_EXCURSION BIT(1) +#define CPPC_PERF_LIMITED_MASK (CPPC_PERF_LIMITED_DESIRED_EXCURSION | \ + CPPC_PERF_LIMITED_MINIMUM_EXCURSION) + /* Each register has the folowing format. */ struct cpc_reg { u8 descriptor; @@ -120,7 +125,6 @@ struct cppc_perf_caps { u32 lowest_nonlinear_perf; u32 lowest_freq; u32 nominal_freq; - bool auto_sel; }; struct cppc_perf_ctrls { @@ -128,6 +132,7 @@ struct cppc_perf_ctrls { u32 min_perf; u32 desired_perf; u32 energy_perf; + bool auto_sel; }; struct cppc_perf_fb_ctrs { @@ -173,12 +178,8 @@ extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window); extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window); extern int cppc_get_auto_sel(int cpu, bool *enable); extern int cppc_set_auto_sel(int cpu, bool enable); -extern int cppc_get_min_perf(int cpu, u64 *min_perf); -extern int cppc_set_min_perf(int cpu, u64 min_perf); -extern int cppc_get_max_perf(int cpu, u64 *max_perf); -extern int cppc_set_max_perf(int cpu, u64 max_perf); extern int cppc_get_perf_limited(int cpu, u64 *perf_limited); -extern int cppc_set_perf_limited(int cpu, u64 perf_limited); +extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear); extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf); extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator); extern int amd_detect_prefcore(bool *detected); @@ -271,27 +272,11 @@ static inline int cppc_set_auto_sel(int cpu, bool enable) { return -EOPNOTSUPP; } -static inline int cppc_get_min_perf(int cpu, u64 *min_perf) -{ - return -EOPNOTSUPP; -} -static inline int cppc_set_min_perf(int cpu, u64 min_perf) -{ - return -EOPNOTSUPP; -} -static inline int cppc_get_max_perf(int cpu, u64 *max_perf) -{ - return -EOPNOTSUPP; -} -static inline int cppc_set_max_perf(int cpu, u64 max_perf) -{ - return -EOPNOTSUPP; -} static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited) { return -EOPNOTSUPP; } -static inline int cppc_set_perf_limited(int cpu, u64 perf_limited) +static inline int cppc_set_perf_limited(int cpu, u64 bits_to_clear) { return -EOPNOTSUPP; }