[PATCH OLK-6.6 00/14] backport cpufreq patches from linux mainline on September 21th.
From: Hongye Lin <linhongye@h-partners.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- backport cpufreq patches from linux mainline on September 21th. Lifeng Zheng (14): ACPI: CPPC: Add IS_OPTIONAL_CPC_REG macro to judge if a cpc_reg is optional ACPI: CPPC: Optimize cppc_get_perf() ACPI: CPPC: Rename cppc_get_perf() to cppc_get_reg_val() ACPI: CPPC: Extract cppc_get_reg_val_in_pcc() ACPI: CPPC: Add cppc_set_reg_val() ACPI: CPPC: Refactor register value get and set ABIs ACPI: CPPC: Prepare cpc_register_resource for Package-type entries ACPI: CPPC: Refactor element parsing into parse_cpc_element() ACPI: CPPC: Refactor resource cleanup into free_reg_resource() ACPI: CPPC: Parse Resource Priority Register entries from _CPC package ACPI: CPPC: Store optional flag in cpc_register_resource ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg() ACPI: CPPC: Add Resource Priority accessors cpufreq: cppc: Expose Resource Priority attributes via sysfs drivers/acpi/cppc_acpi.c | 894 ++++++++++++++++++++++++--------- drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++- include/acpi/cppc_acpi.h | 61 +++ 3 files changed, 1010 insertions(+), 226 deletions(-) -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit e3d7935a6c6138da51626d2b956a079dd0e6671b category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- In ACPI 6.5, s8.4.6.1 _CPC (Continuous Performance Control), whether each of the per-cpu cpc_regs[] is mandatory or optional is defined. Since the CPC_SUPPORTED() check is only for optional _CPC fields, another macro to check if the field is optional is needed. Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-2-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index bd7261503d98..46953aa0117a 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -129,6 +129,20 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); #define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \ !!(cpc)->cpc_entry.int_value : \ !IS_NULL_REG(&(cpc)->cpc_entry.reg)) + +/* + * Each bit indicates the optionality of the register in per-cpu + * cpc_regs[] with the corresponding index. 0 means mandatory and 1 + * means optional. + */ +#define REG_OPTIONAL (0x1FC7D0) + +/* + * Use the index of the register in per-cpu cpc_regs[] to check if + * it's an optional one. + */ +#define IS_OPTIONAL_CPC_REG(reg_idx) (REG_OPTIONAL & (1U << (reg_idx))) + /* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit 45f3763a2122553e548fa0430b77605dc23f00cc category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- Optimize cppc_get_perf() with three changes: 1. Change the error kind to "no such device" when pcc_ss_id < 0, as other register value getting functions. 2. Add a check to ensure the pointer 'perf' is no null. 3. Add a check to verify if the register is supported to be read before using it. The logic is: (1) If the register is of the integer type, check whether the register is optional and its value is 0. If yes, the register is not supported. (2) If the register is of other types, a null one is not supported. 4. Return the result of cpc_read() instead of 0. Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-3-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 46953aa0117a..ad5bba812f30 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1224,6 +1224,9 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_register_resource *reg; + if (perf == NULL) + return -EINVAL; + if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpunum); return -ENODEV; @@ -1231,20 +1234,29 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) reg = &cpc_desc->cpc_regs[reg_idx]; + if ((reg->type == ACPI_TYPE_INTEGER && IS_OPTIONAL_CPC_REG(reg_idx) && + !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER && + IS_NULL_REG(®->cpc_entry.reg))) { + pr_debug("CPC register is not supported\n"); + return -EOPNOTSUPP; + } + if (CPC_IN_PCC(reg)) { int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = 0; + int ret; - if (pcc_ss_id < 0) - return -EIO; + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } pcc_ss_data = pcc_data[pcc_ss_id]; down_write(&pcc_ss_data->pcc_lock); if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - cpc_read(cpunum, reg, perf); + ret = cpc_read(cpunum, reg, perf); else ret = -EIO; @@ -1253,9 +1265,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) return ret; } - cpc_read(cpunum, reg, perf); - - return 0; + return cpc_read(cpunum, reg, perf); } static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit 714d103ce868cd0718d0e5262cbc76384a2eb03a category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- Rename cppc_get_perf() to cppc_get_reg_val() as a generic function to read CPPC registers. Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-4-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index ad5bba812f30..843cd1d29fc8 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1219,16 +1219,16 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } -static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) +static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); struct cpc_register_resource *reg; - if (perf == NULL) + if (val == NULL) return -EINVAL; if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + pr_debug("No CPC descriptor for CPU:%d\n", cpu); return -ENODEV; } @@ -1242,7 +1242,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) } if (CPC_IN_PCC(reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; int ret; @@ -1256,7 +1256,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) down_write(&pcc_ss_data->pcc_lock); if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - ret = cpc_read(cpunum, reg, perf); + ret = cpc_read(cpu, reg, val); else ret = -EIO; @@ -1265,7 +1265,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) return ret; } - return cpc_read(cpunum, reg, perf); + return cpc_read(cpu, reg, val); } static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) @@ -1292,7 +1292,7 @@ int cppc_get_desired_perf(int cpunum, u64 *desired_perf) if (!cppc_desired_perf_readable(cpc_desc)) return -EOPNOTSUPP; - return cppc_get_perf(cpunum, DESIRED_PERF, desired_perf); + return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf); } EXPORT_SYMBOL_GPL(cppc_get_desired_perf); @@ -1305,7 +1305,7 @@ EXPORT_SYMBOL_GPL(cppc_get_desired_perf); */ int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf) { - return cppc_get_perf(cpunum, NOMINAL_PERF, nominal_perf); + return cppc_get_reg_val(cpunum, NOMINAL_PERF, nominal_perf); } /** @@ -1317,7 +1317,7 @@ int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf) */ int cppc_get_highest_perf(int cpunum, u64 *highest_perf) { - return cppc_get_perf(cpunum, HIGHEST_PERF, highest_perf); + return cppc_get_reg_val(cpunum, HIGHEST_PERF, highest_perf); } EXPORT_SYMBOL_GPL(cppc_get_highest_perf); @@ -1330,7 +1330,7 @@ EXPORT_SYMBOL_GPL(cppc_get_highest_perf); */ int cppc_get_epp_perf(int cpunum, u64 *epp_perf) { - return cppc_get_perf(cpunum, ENERGY_PERF, epp_perf); + return cppc_get_reg_val(cpunum, ENERGY_PERF, epp_perf); } EXPORT_SYMBOL_GPL(cppc_get_epp_perf); -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit b5ef45e6a1777fe4e857a1cd48631dc14064eae4 category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- Extract the operations if register is in pcc out from cppc_get_reg_val() as cppc_get_reg_val_in_pcc(). Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-5-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 50 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 843cd1d29fc8..51f3d49807a6 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1219,6 +1219,31 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } +static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val) +{ + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); + struct cppc_pcc_data *pcc_ss_data = NULL; + int ret; + + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } + + pcc_ss_data = pcc_data[pcc_ss_id]; + + down_write(&pcc_ss_data->pcc_lock); + + if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) + ret = cpc_read(cpu, reg, val); + else + ret = -EIO; + + up_write(&pcc_ss_data->pcc_lock); + + return ret; +} + static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) { struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); @@ -1241,29 +1266,8 @@ static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) return -EOPNOTSUPP; } - if (CPC_IN_PCC(reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret; - - if (pcc_ss_id < 0) { - pr_debug("Invalid pcc_ss_id\n"); - return -ENODEV; - } - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - - if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - ret = cpc_read(cpu, reg, val); - else - ret = -EIO; - - up_write(&pcc_ss_data->pcc_lock); - - return ret; - } + if (CPC_IN_PCC(reg)) + return cppc_get_reg_val_in_pcc(cpu, reg, val); return cpc_read(cpu, reg, val); } -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit e05c75072c2eaa6e0b152a558b3be5cbcf79a587 category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- Add cppc_set_reg_val() as a generic function for setting CPPC register values, with this features: 1. Check register. If a register is writeable, it must be a buffer and can not be null. 2. Extract the operations if register is in PCC out as cppc_set_reg_val_in_pcc(). This function can be used to reduce some existing code duplication. Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-6-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 51f3d49807a6..8d8c84944fd1 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1277,6 +1277,55 @@ static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) return cpc_desc->version < CPPC_V4_REV; } +static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val) +{ + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); + struct cppc_pcc_data *pcc_ss_data = NULL; + int ret; + + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } + + ret = cpc_write(cpu, reg, val); + if (ret) + return ret; + + pcc_ss_data = pcc_data[pcc_ss_id]; + + down_write(&pcc_ss_data->pcc_lock); + /* 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); + + return ret; +} + +static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *reg; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + reg = &cpc_desc->cpc_regs[reg_idx]; + + /* if a register is writeable, it must be a buffer and not null */ + if ((reg->type != ACPI_TYPE_BUFFER) || IS_NULL_REG(®->cpc_entry.reg)) { + pr_debug("CPC register is not supported\n"); + return -EOPNOTSUPP; + } + + if (CPC_IN_PCC(reg)) + return cppc_set_reg_val_in_pcc(cpu, reg, val); + + return cpc_write(cpu, reg, val); +} + /** * cppc_get_desired_perf - Get the desired performance register value. * @cpunum: CPU from which to get desired performance. -- 2.33.0
mainline inclusion from mainline-v6.16-rc1 commit ab482f1bac6b128b8fe910b6663a4f74a3a9796c category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... ---------------------------------------------------------------------- Refactor register value get and set ABIs by using cppc_get_reg_val(), cppc_set_reg_val() and CPPC_REG_VAL_READ(). Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patch.msgid.link/20250411093855.982491-7-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 111 +++------------------------------------ 1 file changed, 7 insertions(+), 104 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 8d8c84944fd1..daa030a8b8fa 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1871,44 +1871,14 @@ EXPORT_SYMBOL_GPL(cppc_set_auto_sel_caps); */ int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); - struct cpc_register_resource *auto_sel_reg; - u64 auto_sel; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpunum); - return -ENODEV; - } - - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; - - if (!CPC_SUPPORTED(auto_sel_reg)) - pr_warn_once("Autonomous mode is not unsupported!\n"); - - if (CPC_IN_PCC(auto_sel_reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = 0; - - if (pcc_ss_id < 0) - return -ENODEV; - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - - if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) { - cpc_read(cpunum, auto_sel_reg, &auto_sel); - perf_caps->auto_sel = (bool)auto_sel; - } else { - ret = -EIO; - } - - up_write(&pcc_ss_data->pcc_lock); + u64 auto_sel; + int ret; + ret = cppc_get_reg_val(cpunum, AUTO_SEL_ENABLE, &auto_sel); + if (ret) return ret; - } + perf_caps->auto_sel = (bool)auto_sel; return 0; } EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps); @@ -1920,43 +1890,7 @@ EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps); */ int cppc_set_auto_sel(int cpu, bool enable) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - 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; - int ret = -EINVAL; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -ENODEV; - } - - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; - - if (CPC_IN_PCC(auto_sel_reg)) { - if (pcc_ss_id < 0) { - pr_debug("Invalid pcc_ss_id\n"); - return -ENODEV; - } - - if (CPC_SUPPORTED(auto_sel_reg)) { - ret = cpc_write(cpu, auto_sel_reg, enable); - if (ret) - return ret; - } - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - /* 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 { - ret = -ENOTSUPP; - pr_debug("_CPC in PCC is not supported\n"); - } - - return ret; + return cppc_set_reg_val(cpu, AUTO_SEL_ENABLE, enable); } EXPORT_SYMBOL_GPL(cppc_set_auto_sel); @@ -1970,38 +1904,7 @@ EXPORT_SYMBOL_GPL(cppc_set_auto_sel); */ int cppc_set_enable(int cpu, bool enable) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - struct cpc_register_resource *enable_reg; - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = -EINVAL; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -EINVAL; - } - - enable_reg = &cpc_desc->cpc_regs[ENABLE]; - - if (CPC_IN_PCC(enable_reg)) { - - if (pcc_ss_id < 0) - return -EIO; - - ret = cpc_write(cpu, enable_reg, enable); - if (ret) - return ret; - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - /* after writing CPC, transfer the ownership of PCC to platfrom */ - ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE); - up_write(&pcc_ss_data->pcc_lock); - return ret; - } - - return cpc_write(cpu, enable_reg, enable); + return cppc_set_reg_val(cpu, ENABLE, enable); } EXPORT_SYMBOL_GPL(cppc_set_enable); -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) introduces the Resource Priority entry, which is a Package of sub-packages rather than a plain Integer or Buffer register. The existing cpc_register_resource union only accommodates Integer and Buffer (register descriptor) fields. Add a Package variant to the cpc_entry union so that nested structures such as RESOURCE_PRIORITY can store their element count and a dynamically allocated array of child cpc_register_resource descriptors. No functional change intended; the new Package fields are not yet consumed by any caller. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- include/acpi/cppc_acpi.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index b9787f77fe54..cf5f3780f229 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -56,6 +56,14 @@ struct cpc_register_resource { union { struct cpc_reg reg; u64 int_value; + /* + * CPPC v4: nested Package (e.g. RESOURCE_PRIORITY), + * elements dynamically allocated + */ + struct { + u32 count; + struct cpc_register_resource *elements; + } package; } cpc_entry; }; -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- acpi_cppc_processor_probe() contains a large inline block (~70 lines) that handles ACPI_TYPE_INTEGER and ACPI_TYPE_BUFFER entries in the _CPC package. This block will need to be reused for parsing individual elements inside the nested RESOURCE_PRIORITY sub-packages that CPPC v4 defines (Section 8.4.6.1.2.7). Extract the Integer / Buffer handling into a standalone parse_cpc_element() helper so that both the top-level _CPC loop and the upcoming package parser can share the same logic for register validation, PCC subspace tracking, SystemMemory ioremap, and SystemIO / FFH checks. No functional change; the new function reproduces the original behaviour exactly. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 186 +++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 78 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index daa030a8b8fa..b3156cb4507f 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -659,6 +659,109 @@ static int pcc_data_alloc(int pcc_ss_id) return 0; } +/** + * parse_cpc_element - Parse a single CPC element into a cpc_register_resource. + * @cpc_obj: Pointer to the ACPI object representing the CPC element. + * @cpc_reg: Output CPC register resource to populate. + * @pcc_subspace_id: In/out pointer to PCC subspace ID; extracted once on first + * PCC-type register and validated for consistency thereafter. + * @cpu: CPU number, used for debug messages. + * @entry_num: Index within the CPC table entries, used for diagnostics. + * + * Handles ACPI_TYPE_INTEGER (static value) and ACPI_TYPE_BUFFER (register + * descriptor). Sets up PCC subspace tracking, ioremap for SystemMemory, + * and validates SystemIO / FFH register parameters. + * + * Return: 0 on success, -ENODATA on invalid or unsupported data. + */ +static int parse_cpc_element(union acpi_object *cpc_obj, + struct cpc_register_resource *cpc_reg, + int *pcc_subspace_id, u32 cpu, unsigned int entry_num) +{ + struct cpc_reg *gas_t; + + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + cpc_reg->type = ACPI_TYPE_INTEGER; + cpc_reg->cpc_entry.int_value = cpc_obj->integer.value; + } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { + gas_t = (struct cpc_reg *)cpc_obj->buffer.pointer; + + /* + * The PCC Subspace index is encoded inside + * the CPC table entries. The same PCC index + * will be used for all the PCC entries, + * so extract it only once. + */ + if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + if (*pcc_subspace_id < 0) { + *pcc_subspace_id = gas_t->access_width; + if (pcc_data_alloc(*pcc_subspace_id)) + return -ENODATA; + } else if (*pcc_subspace_id != gas_t->access_width) { + pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n", + cpu); + return -ENODATA; + } + } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + if (gas_t->address) { + void __iomem *addr; + size_t access_width; + + if (!osc_cpc_flexible_adr_space_confirmed) { + pr_debug("Flexible address space capability not supported\n"); + if (!cpc_supported_by_cpu()) + return -ENODATA; + } + + access_width = GET_BIT_WIDTH(gas_t) / 8; + addr = ioremap(gas_t->address, access_width); + if (!addr) + return -ENODATA; + cpc_reg->sys_mem_vaddr = addr; + } + } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + if (gas_t->access_width < 1 || gas_t->access_width > 3) { + /* + * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit. + * SystemIO doesn't implement 64-bit + * registers. + */ + pr_debug("Invalid access width %d for SystemIO register in _CPC\n", + gas_t->access_width); + return -ENODATA; + } + if (gas_t->address & OVER_16BTS_MASK) { + /* SystemIO registers use 16-bit integer addresses */ + pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n", + gas_t->address); + return -ENODATA; + } + if (!osc_cpc_flexible_adr_space_confirmed) { + pr_debug("Flexible address space capability not supported\n"); + if (!cpc_supported_by_cpu()) + return -ENODATA; + } + } else { + if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || + !cpc_ffh_supported()) { + /* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */ + pr_debug("Unsupported register type (%d) in _CPC\n", + gas_t->space_id); + return -ENODATA; + } + } + + cpc_reg->type = ACPI_TYPE_BUFFER; + memcpy(&cpc_reg->cpc_entry.reg, gas_t, sizeof(*gas_t)); + } else { + pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n", + entry_num, cpu); + return -ENODATA; + } + + return 0; +} + /* * An example CPC table looks like the following. * @@ -703,7 +806,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *out_obj, *cpc_obj; struct cpc_desc *cpc_ptr; - struct cpc_reg *gas_t; struct device *cpu_dev; acpi_handle handle = pr->handle; unsigned int num_ent, i, cpc_rev; @@ -789,80 +891,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) for (i = 2; i < num_ent; i++) { cpc_obj = &out_obj->package.elements[i]; - if (cpc_obj->type == ACPI_TYPE_INTEGER) { - cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; - cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value; - } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { - gas_t = (struct cpc_reg *) - cpc_obj->buffer.pointer; - - /* - * The PCC Subspace index is encoded inside - * the CPC table entries. The same PCC index - * will be used for all the PCC entries, - * so extract it only once. - */ - if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - if (pcc_subspace_id < 0) { - pcc_subspace_id = gas_t->access_width; - if (pcc_data_alloc(pcc_subspace_id)) - goto out_free; - } else if (pcc_subspace_id != gas_t->access_width) { - pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n", - pr->id); - goto out_free; - } - } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { - if (gas_t->address) { - void __iomem *addr; - size_t access_width; - - if (!osc_cpc_flexible_adr_space_confirmed) { - pr_debug("Flexible address space capability not supported\n"); - if (!cpc_supported_by_cpu()) - goto out_free; - } - - access_width = GET_BIT_WIDTH(gas_t) / 8; - addr = ioremap(gas_t->address, access_width); - if (!addr) - goto out_free; - cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; - } - } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { - if (gas_t->access_width < 1 || gas_t->access_width > 3) { - /* - * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit. - * SystemIO doesn't implement 64-bit - * registers. - */ - pr_debug("Invalid access width %d for SystemIO register in _CPC\n", - gas_t->access_width); - goto out_free; - } - if (gas_t->address & OVER_16BTS_MASK) { - /* SystemIO registers use 16-bit integer addresses */ - pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n", - gas_t->address); - goto out_free; - } - if (!osc_cpc_flexible_adr_space_confirmed) { - pr_debug("Flexible address space capability not supported\n"); - if (!cpc_supported_by_cpu()) - goto out_free; - } - } else { - if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) { - /* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */ - pr_debug("Unsupported register type (%d) in _CPC\n", - gas_t->space_id); - goto out_free; - } - } - - cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; - memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); - } else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { + if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { /* * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a * Package of Resource Priority Register Descriptor sub-packages. @@ -873,9 +902,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0; } else { - pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n", - i, pr->id); - goto out_free; + ret = parse_cpc_element(cpc_obj, &cpc_ptr->cpc_regs[i-2], + &pcc_subspace_id, pr->id, i); + if (ret) + goto out_free; } } per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id; -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- Both the error path of acpi_cppc_processor_probe() and the normal cleanup in acpi_cppc_processor_exit() iterate over the cpc_regs[] array and iounmap() any SystemMemory virtual addresses that were set up during probe. CPPC v4 adds Package-type entries that own dynamically allocated sub-elements which also need to be freed. Inlining this recursive cleanup at every call-site would be error-prone and repetitive. Extract the per-register cleanup logic into free_reg_resource(), which releases any iomapped address and, for Package-type entries, recursively frees all child elements and the elements array itself. Convert both the probe error path and _exit() to use the new helper. No functional change for existing Integer / Buffer entries. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 41 ++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index b3156cb4507f..0bdd8de2c7b9 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -762,6 +762,30 @@ static int parse_cpc_element(union acpi_object *cpc_obj, return 0; } +/** + * free_reg_resource - Free resources held by a CPC register resource. + * @cpc_reg: Pointer to the CPC register resource to clean up. + * + * Releases any iomapped SystemMemory address and, for Package-type + * resources, recursively frees all nested elements before freeing the + * elements array itself. + */ +static void free_reg_resource(struct cpc_register_resource *cpc_reg) +{ + void __iomem *addr = cpc_reg->sys_mem_vaddr; + int i; + + if (addr) + iounmap(addr); + + if (cpc_reg->type == ACPI_TYPE_PACKAGE) { + for (i = 0; i < cpc_reg->cpc_entry.package.count; i++) + free_reg_resource(&cpc_reg->cpc_entry.package.elements[i]); + + kfree(cpc_reg->cpc_entry.package.elements); + } +} + /* * An example CPC table looks like the following. * @@ -968,12 +992,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) out_free: /* Free all the mapped sys mem areas for this CPU */ - for (i = 2; i < cpc_ptr->num_entries; i++) { - void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr; + for (i = 2; i < cpc_ptr->num_entries; i++) + free_reg_resource(&cpc_ptr->cpc_regs[i-2]); - if (addr) - iounmap(addr); - } kfree(cpc_ptr); out_buf_free: @@ -992,7 +1013,6 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) { struct cpc_desc *cpc_ptr; unsigned int i; - void __iomem *addr; int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id); if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) { @@ -1010,12 +1030,9 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) if (!cpc_ptr) return; - /* Free all the mapped sys mem areas for this CPU */ - for (i = 2; i < cpc_ptr->num_entries; i++) { - addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr; - if (addr) - iounmap(addr); - } + /* Free all the mapped sys mem areas and nested package resources for this CPU */ + for (i = 2; i < cpc_ptr->num_entries; i++) + free_reg_resource(&cpc_ptr->cpc_regs[i-2]); kobject_put(&cpc_ptr->kobj); kfree(cpc_ptr); -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority entry as a Package of sub-packages, each containing: - CONTROLLED_RESOURCES: a Package of integer resource IDs - ENABLE_VALUE / ENABLE_REGISTER: enable/disable control - PRIORITY_COUNT / PRIORITY_REGISTER: priority level setting These allow OSPM to set relative priority among processors for shared resources such as boost, throttle, L2/L3 cache, and memory bandwidth. Implement parse_priority_regs() which: 1. Validates each sub-package has the expected element count (RESOURCE_PRIORITY_NUM). 2. Allocates cpc_register_resource arrays for the sub-package elements and the nested CONTROLLED_RESOURCES list. 3. Parses CONTROLLED_RESOURCES as integers and the remaining entries (ENABLE_REGISTER, PRIORITY_REGISTER, etc.) via parse_cpc_element() so that register descriptors, PCC subspace tracking, and ioremap are handled consistently. 4. Wires the parser into the main _CPC probe loop, replacing the previous "package type not supported" stub with full parsing for RESOURCE_PRIORITY while rejecting unexpected Package entries. The probe and _exit() error/cleanup paths already use free_reg_resource(), which recursively frees Package-type entries, so no additional cleanup changes are needed. Add the resource_priority_regs enumeration that defines the indices into each Resource Priority sub-package (CONTROLLED_RESOURCES, ENABLE_VALUE, ENABLE_REGISTER, PRIORITY_COUNT, PRIORITY_REGISTER) and RESOURCE_PRIORITY_NUM as the element count sentinel. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 140 ++++++++++++++++++++++++++++++++++++--- include/acpi/cppc_acpi.h | 13 ++++ 2 files changed, 145 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 0bdd8de2c7b9..a62fa4d4d905 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -786,6 +786,93 @@ static void free_reg_resource(struct cpc_register_resource *cpc_reg) } } +/** + * parse_priority_regs - Parse the RESOURCE_PRIORITY nested package structure. + * @cpc_obj: ACPI Package object for the RESOURCE_PRIORITY entry. + * @regs: Output array of cpc_register_resource to fill. + * @pcc_subspace_id: In/out pointer to PCC subspace ID. + * @cpu: CPU number, used for debug messages. + * + * The RESOURCE_PRIORITY entry (CPPC v4) is a Package of sub-packages. + * Each sub-package has RESOURCE_PRIORITY_NUM elements: + * [0] = Package of integers (CONTROLLED_RESOURCES list) + * [1] = ENABLE_VALUE, [2] = ENABLE_REGISTER, + * [3] = PRIORITY_COUNT, [4] = PRIORITY_REGISTER + * + * Return: 0 on success, -ENODATA on malformed data, -ENOMEM on allocation failure. + */ +static int parse_priority_regs(union acpi_object *cpc_obj, + struct cpc_register_resource *regs, + int *pcc_subspace_id, u32 cpu) +{ + struct cpc_register_resource *reg_elements; + union acpi_object reg_desc_obj; + unsigned int i, j, resources_count; + int ret; + + for (i = 0; i < cpc_obj->package.count; i++) { + reg_desc_obj = cpc_obj->package.elements[i]; + if (reg_desc_obj.type != ACPI_TYPE_PACKAGE || + reg_desc_obj.package.count != RESOURCE_PRIORITY_NUM) { + pr_debug("Malformed priority regs sub-pkg: type %d count %d, expected %d for CPU:%d\n", + reg_desc_obj.type, reg_desc_obj.package.count, + RESOURCE_PRIORITY_NUM, cpu); + return -ENODATA; + } + + reg_elements = kzalloc(RESOURCE_PRIORITY_NUM * + sizeof(struct cpc_register_resource), GFP_KERNEL); + if (!reg_elements) { + pr_debug("Failed to allocate reg_elements for CPU:%d\n", cpu); + return -ENOMEM; + } + + /* + * Assign values immediately after successful allocation to ensure that resources + * can be properly released. + */ + regs[i].type = ACPI_TYPE_PACKAGE; + regs[i].cpc_entry.package.count = RESOURCE_PRIORITY_NUM; + regs[i].cpc_entry.package.elements = reg_elements; + + resources_count = reg_desc_obj.package.elements[0].package.count; + + if (reg_desc_obj.package.elements[0].type != ACPI_TYPE_PACKAGE || + !resources_count) { + pr_debug("Invalid priority sub-elements: type %d count %d for CPU:%d\n", + reg_desc_obj.package.elements[0].type, resources_count, cpu); + return -ENODATA; + } + + reg_elements[0].cpc_entry.package.elements = + kzalloc(resources_count * sizeof(struct cpc_register_resource), + GFP_KERNEL); + if (!reg_elements[0].cpc_entry.package.elements) { + pr_debug("Failed to allocate %d priority sub-elements for CPU:%d\n", + resources_count, cpu); + return -ENOMEM; + } + + reg_elements[0].type = ACPI_TYPE_PACKAGE; + reg_elements[0].cpc_entry.package.count = resources_count; + + for (j = 0; j < reg_elements[0].cpc_entry.package.count; j++) { + reg_elements[0].cpc_entry.package.elements[j].type = ACPI_TYPE_INTEGER; + reg_elements[0].cpc_entry.package.elements[j].cpc_entry.int_value = + reg_desc_obj.package.elements[0].package.elements[j].integer.value; + } + + for (j = 1; j < RESOURCE_PRIORITY_NUM; j++) { + ret = parse_cpc_element(®_desc_obj.package.elements[j], ®_elements[j], + pcc_subspace_id, cpu, RESOURCE_PRIORITY); + if (ret) + return ret; + } + } + + return 0; +} + /* * An example CPC table looks like the following. * @@ -828,6 +915,7 @@ static inline void arch_init_invariance_cppc(void) { } int acpi_cppc_processor_probe(struct acpi_processor *pr) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + struct cpc_register_resource *pkg_elements; union acpi_object *out_obj, *cpc_obj; struct cpc_desc *cpc_ptr; struct device *cpu_dev; @@ -836,6 +924,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) int pcc_subspace_id = -1; acpi_status status; int ret = -ENODATA; + u32 pkg_count; if (!osc_sb_cppc2_support_acked) { pr_debug("CPPC v2 _OSC not acked\n"); @@ -915,16 +1004,51 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) for (i = 2; i < num_ent; i++) { cpc_obj = &out_obj->package.elements[i]; - if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { + /* + * Package-type entries are used for nested structures such as + * RESOURCE_PRIORITY (CPPC v4). Only RESOURCE_PRIORITY is + * currently supported; any other Package entry is rejected. + */ + if (cpc_obj->type == ACPI_TYPE_PACKAGE) { + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_PACKAGE; + cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = 0; + cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = NULL; + + pkg_count = cpc_obj->package.count; + if (!pkg_count) { + pr_debug("Empty package entry at index %d for CPU:%d\n", + i, pr->id); + continue; + } + + pkg_elements = kzalloc(pkg_count * sizeof(struct cpc_register_resource), + GFP_KERNEL); + if (!pkg_elements) { + ret = -ENOMEM; + goto out_free; + } + /* - * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a - * Package of Resource Priority Register Descriptor sub-packages. - * Parsing the full structure is not yet supported. - * Mark the register as unsupported for now. + * Assign values immediately after successful allocation to ensure that + * resources can be properly released. */ - pr_debug("CPU:%d Resource Priority not supported\n", pr->id); - cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; - cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0; + cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = pkg_count; + cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = pkg_elements; + + if (i - 2 == RESOURCE_PRIORITY) { + ret = parse_priority_regs(cpc_obj, pkg_elements, + &pcc_subspace_id, pr->id); + if (ret) + goto out_free; + + pr_debug("Parsed RESOURCE_PRIORITY (%d sub-pkgs) for CPU:%d\n", + pkg_count, pr->id); + } else { + pr_debug("Unexpected ACPI_TYPE_PACKAGE at index %d for CPU:%d\n", + i, pr->id); + ret = -ENODATA; + goto out_free; + } } else { ret = parse_cpc_element(cpc_obj, &cpc_ptr->cpc_regs[i-2], &pcc_subspace_id, pr->id, i); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index cf5f3780f229..91c523677df1 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -108,6 +108,19 @@ enum cppc_regs { RESOURCE_PRIORITY, }; +/* + * Indices into each sub-package of the RESOURCE_PRIORITY entry. + * RESOURCE_PRIORITY_NUM serves as the element count / loop bound. + */ +enum resource_priority_regs { + CONTROLLED_RESOURCES, /* Package of integer resource IDs */ + ENABLE_VALUE, /* Enable/disable value */ + ENABLE_REGISTER, /* Register for enable/disable control */ + PRIORITY_COUNT, /* Number of priority levels */ + PRIORITY_REGISTER, /* Register for priority setting */ + RESOURCE_PRIORITY_NUM, /* Number of elements (sentinel) */ +}; + /* * Categorization of registers as described * in the ACPI v.5.1 spec. -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- The current optionality check uses a compile-time bitmask (REG_OPTIONAL) applied at call sites via IS_OPTIONAL_CPC_REG(reg_idx). This requires every caller to know the register index, which will not work for registers accessed without a fixed index (e.g. entries inside Resource Priority sub-packages). Add a boolean 'optional' field to cpc_register_resource so that each register element carries its own optionality. Populate the field during _CPC probe (for main registers) and parse_priority_regs() (for Resource Priority sub-packages), using the existing REG_OPTIONAL and RES_PRIO_OPTIONAL bitmasks respectively. Replace the IS_OPTIONAL_CPC_REG() check in cppc_get_reg_val() with a direct test of reg->optional, and remove the IS_OPTIONAL_CPC_REG() macro. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 14 +++++++++----- include/acpi/cppc_acpi.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index a62fa4d4d905..4634ee599fe1 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -138,10 +138,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); #define REG_OPTIONAL (0x1FC7D0) /* - * Use the index of the register in per-cpu cpc_regs[] to check if - * it's an optional one. + * Each bit indicates the optionality of the register in resource + * priority register descriptor with the corresponding index. 0 means + * mandatory and 1 means optional. */ -#define IS_OPTIONAL_CPC_REG(reg_idx) (REG_OPTIONAL & (1U << (reg_idx))) +#define RES_PRIO_OPTIONAL (0x6) /* * Arbitrary Retries in case the remote processor is slow to respond @@ -854,7 +855,7 @@ static int parse_priority_regs(union acpi_object *cpc_obj, } reg_elements[0].type = ACPI_TYPE_PACKAGE; - reg_elements[0].cpc_entry.package.count = resources_count; + reg_elements[0].optional = RES_PRIO_OPTIONAL & 1U; for (j = 0; j < reg_elements[0].cpc_entry.package.count; j++) { reg_elements[0].cpc_entry.package.elements[j].type = ACPI_TYPE_INTEGER; @@ -863,6 +864,7 @@ static int parse_priority_regs(union acpi_object *cpc_obj, } for (j = 1; j < RESOURCE_PRIORITY_NUM; j++) { + reg_elements[j].optional = RES_PRIO_OPTIONAL & (1U << j); ret = parse_cpc_element(®_desc_obj.package.elements[j], ®_elements[j], pcc_subspace_id, cpu, RESOURCE_PRIORITY); if (ret) @@ -1003,6 +1005,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* Iterate through remaining entries in _CPC */ for (i = 2; i < num_ent; i++) { cpc_obj = &out_obj->package.elements[i]; + cpc_ptr->cpc_regs[i-2].optional = REG_OPTIONAL & (1U << (i-2)); /* * Package-type entries are used for nested structures such as @@ -1064,6 +1067,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) * LOWEST_FREQ and NOMINAL_FREQ regs as unsupported */ for (i = num_ent - 2; i < MAX_CPC_REG_ENT; i++) { + cpc_ptr->cpc_regs[i].optional = true; cpc_ptr->cpc_regs[i].type = ACPI_TYPE_INTEGER; cpc_ptr->cpc_regs[i].cpc_entry.int_value = 0; } @@ -1430,7 +1434,7 @@ static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) reg = &cpc_desc->cpc_regs[reg_idx]; - if ((reg->type == ACPI_TYPE_INTEGER && IS_OPTIONAL_CPC_REG(reg_idx) && + if ((reg->type == ACPI_TYPE_INTEGER && reg->optional && !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER && IS_NULL_REG(®->cpc_entry.reg))) { pr_debug("CPC register is not supported\n"); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 91c523677df1..62cba043b438 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -65,6 +65,7 @@ struct cpc_register_resource { struct cpc_register_resource *elements; } package; } cpc_entry; + bool optional; }; /* Container to hold the CPC details for each CPU */ -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- cppc_get_reg_val() and cppc_set_reg_val() combine two responsibilities: looking up the per-CPU cpc_desc and then performing the actual register read/write (including null/optional checks and PCC handling). Split out the register I/O logic into cpc_read_reg() and cpc_write_reg() that accept a struct cpc_register_resource pointer directly. This allows callers that already hold a register reference -- such as the upcoming Resource Priority accessors -- to read or write a register without going through the per-CPU descriptor lookup by index. Also rename the PCC wrappers from cppc_get/set_reg_val_in_pcc() to cpc_read/write_in_pcc() to align with the new naming convention. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 89 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 4634ee599fe1..d9e4c39f9152 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1394,7 +1394,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } -static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val) +static int cpc_read_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val) { int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; @@ -1419,32 +1419,49 @@ static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u return ret; } -static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) +/** + * cpc_read_reg - Read value from a register element that may be Integer or Buffer. + * @cpu: CPU number. + * @reg: Pointer to the CPC register element. + * @val: Output value. + * + * Return: 0 on success, -EOPNOTSUPP if null/unsupported, negative on error. + */ +static int cpc_read_reg(int cpu, struct cpc_register_resource *reg, u64 *val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cpc_register_resource *reg; - if (val == NULL) return -EINVAL; - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -ENODEV; - } - - reg = &cpc_desc->cpc_regs[reg_idx]; - - if ((reg->type == ACPI_TYPE_INTEGER && reg->optional && - !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER && - IS_NULL_REG(®->cpc_entry.reg))) { - pr_debug("CPC register is not supported\n"); - return -EOPNOTSUPP; + if (reg->type == ACPI_TYPE_INTEGER) { + if (reg->optional && !reg->cpc_entry.int_value) + goto err_unsupported; + } else if (reg->type == ACPI_TYPE_BUFFER) { + if (IS_NULL_REG(®->cpc_entry.reg)) + goto err_unsupported; + } else { + goto err_unsupported; } if (CPC_IN_PCC(reg)) - return cppc_get_reg_val_in_pcc(cpu, reg, val); + return cpc_read_in_pcc(cpu, reg, val); return cpc_read(cpu, reg, val); + +err_unsupported: + pr_debug("CPC register is not supported\n"); + return -EOPNOTSUPP; +} + +static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + return cpc_read_reg(cpu, &cpc_desc->cpc_regs[reg_idx], val); } static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) @@ -1452,7 +1469,7 @@ static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) return cpc_desc->version < CPPC_V4_REV; } -static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val) +static int cpc_write_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val) { int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; @@ -1477,18 +1494,16 @@ static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u return ret; } -static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) +/** + * cpc_write_reg - Write a CPC register. + * @cpu: CPU number. + * @reg: Pointer to the CPC register resource. + * @val: Value to write. + * + * Return: 0 on success, negative error code otherwise. + */ +static int cpc_write_reg(int cpu, struct cpc_register_resource *reg, u64 val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cpc_register_resource *reg; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -ENODEV; - } - - reg = &cpc_desc->cpc_regs[reg_idx]; - /* if a register is writeable, it must be a buffer and not null */ if ((reg->type != ACPI_TYPE_BUFFER) || IS_NULL_REG(®->cpc_entry.reg)) { pr_debug("CPC register is not supported\n"); @@ -1496,11 +1511,23 @@ static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) } if (CPC_IN_PCC(reg)) - return cppc_set_reg_val_in_pcc(cpu, reg, val); + return cpc_write_in_pcc(cpu, reg, val); return cpc_write(cpu, reg, val); } +static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + return cpc_write_reg(cpu, &cpc_desc->cpc_regs[reg_idx], val); +} + /** * cppc_get_desired_perf - Get the desired performance register value. * @cpunum: CPU from which to get desired performance. -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority mechanism that lets OSPM control relative priority among processors for shared resources such as processor boost, throttle, L2/L3 cache, and memory bandwidth. Add the following exported APIs for user-space and driver consumers: - cppc_get_resource_priority_count() -- number of sub-packages - cppc_get_resource_priority_resources() -- resource type IDs - cppc_get/set_res_priority_enable() -- enable/disable a group - cppc_get_res_priority_count() -- priority levels in a group - cppc_get/set_res_priority() -- read/write the priority value These wrappers navigate the nested Package structure parsed earlier by parse_priority_regs() and delegate register I/O to cpc_read_reg() / cpc_write_reg(). Provide stubs returning -EOPNOTSUPP for the !CONFIG_ACPI_CPPC_LIB case. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/acpi/cppc_acpi.c | 264 +++++++++++++++++++++++++++++++++++++++ include/acpi/cppc_acpi.h | 38 ++++++ 2 files changed, 302 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index d9e4c39f9152..2cc8753d2717 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -2242,6 +2242,270 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) } EXPORT_SYMBOL_GPL(cppc_set_perf); +/** + * get_res_prio_subpkg - Get pointer to the elements array of a RESOURCE_PRIORITY sub-package. + * @cpu: CPU number. + * @index: Sub-package index (0 to count-1). + * + * Return: Pointer to the sub-package's elements array, or NULL on error. + * + * The layout within each sub-package element is: + * elements[CONTROLLED_RESOURCES] = [0] + * elements[ENABLE_VALUE] = [1] + * elements[ENABLE_REGISTER] = [2] + * elements[PRIORITY_COUNT] = [3] + * elements[PRIORITY_REGISTER] = [4] + */ +static struct cpc_register_resource *get_res_prio_subpkg(int cpu, int index) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *rp_pkg; + + if (!cpc_desc) + return NULL; + + rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY]; + if (rp_pkg->type != ACPI_TYPE_PACKAGE) + return NULL; + + if (index < 0 || index >= rp_pkg->cpc_entry.package.count) + return NULL; + + return rp_pkg->cpc_entry.package.elements[index].cpc_entry.package.elements; +} + +/** + * cppc_get_resource_priority_count - Get number of Resource Priority sub-packages. + * @cpu: CPU number. + * @count: Output number of resource priority groups. + * + * Return: 0 on success, -EOPNOTSUPP if RESOURCE_PRIORITY not provided by firmware. + */ +int cppc_get_resource_priority_count(int cpu, int *count) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *rp_pkg; + + if (!count) + return -EINVAL; + + if (!cpc_desc) + return -ENODEV; + + rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY]; + if (rp_pkg->type != ACPI_TYPE_PACKAGE) + return -EOPNOTSUPP; + + *count = rp_pkg->cpc_entry.package.count; + if (*count <= 0) + return -EOPNOTSUPP; + + return 0; +} +EXPORT_SYMBOL_GPL(cppc_get_resource_priority_count); + +/** + * cppc_get_resource_priority_resources - Read Controlled Resources list for a sub-package. + * @cpu: CPU number. + * @index: Sub-package index (0 to count-1). + * @resources: Output array of resource type IDs (caller-allocated). + * @num_resources: Input = array capacity, output = actual count. + * + * Return: 0 on success, -EOPNOTSUPP, -EINVAL, etc. + */ +int cppc_get_resource_priority_resources(int cpu, int index, + u32 *resources, int *num_resources) +{ + struct cpc_register_resource *elems; + struct cpc_register_resource *cr_pkg; + int i, cr_count; + + if (!resources || !num_resources || *num_resources <= 0) + return -EINVAL; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + cr_pkg = &elems[CONTROLLED_RESOURCES]; + if (cr_pkg->type != ACPI_TYPE_PACKAGE) + return -EOPNOTSUPP; + + cr_count = cr_pkg->cpc_entry.package.count; + if (cr_count <= 0) { + *num_resources = 0; + return 0; + } + + *num_resources = min(cr_count, *num_resources); + + for (i = 0; i < *num_resources; i++) + resources[i] = cr_pkg->cpc_entry.package.elements[i].cpc_entry.int_value; + + return 0; +} +EXPORT_SYMBOL_GPL(cppc_get_resource_priority_resources); + +/** + * cppc_get_res_priority_enable - Read enable state of a Resource Priority register. + * @cpu: CPU number. + * @index: Sub-package index. + * @enable: Output true if enabled, false if disabled. + * + * Compares the current ENABLE_REGISTER value against ENABLE_VALUE. + * If ENABLE_REGISTER is null/unsupported, returns -EOPNOTSUPP. + * + * Return: 0 on success, negative error otherwise. + */ +int cppc_get_res_priority_enable(int cpu, int index, bool *enable) +{ + struct cpc_register_resource *elems; + struct cpc_register_resource *enable_reg; + u64 reg_val, enable_val; + int ret; + + if (!enable) + return -EINVAL; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + enable_reg = &elems[ENABLE_REGISTER]; + if (enable_reg->type != ACPI_TYPE_BUFFER || + IS_NULL_REG(&enable_reg->cpc_entry.reg)) + return -EOPNOTSUPP; + + ret = cpc_read_reg(cpu, enable_reg, ®_val); + if (ret) + return ret; + + ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &enable_val); + if (ret) + return ret; + + *enable = (reg_val == enable_val); + return 0; +} +EXPORT_SYMBOL_GPL(cppc_get_res_priority_enable); + +/** + * cppc_set_res_priority_enable - Set enable state of a Resource Priority register. + * @cpu: CPU number. + * @index: Sub-package index. + * @enable: true to enable (write ENABLE_VALUE), false to disable (write 0). + * + * Return: 0 on success, negative error otherwise. + */ +int cppc_set_res_priority_enable(int cpu, int index, bool enable) +{ + struct cpc_register_resource *elems; + struct cpc_register_resource *enable_reg; + u64 val; + int ret; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + enable_reg = &elems[ENABLE_REGISTER]; + + if (enable) { + ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &val); + if (ret) + return ret; + } else { + val = 0; + } + + return cpc_write_reg(cpu, enable_reg, val); +} +EXPORT_SYMBOL_GPL(cppc_set_res_priority_enable); + +/** + * cppc_get_res_priority_count - Read priority count for a Resource Priority register. + * @cpu: CPU number. + * @index: Sub-package index. + * @count: Output priority count (>= 2 per spec). + * + * Return: 0 on success, negative error otherwise. + */ +int cppc_get_res_priority_count(int cpu, int index, u64 *count) +{ + struct cpc_register_resource *elems; + + if (!count) + return -EINVAL; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + return cpc_read_reg(cpu, &elems[PRIORITY_COUNT], count); +} +EXPORT_SYMBOL_GPL(cppc_get_res_priority_count); + +/** + * cppc_get_res_priority - Read priority value for a Resource Priority register. + * @cpu: CPU number. + * @index: Sub-package index. + * @priority: Output priority value. + * + * Return: 0 on success, negative error otherwise. + */ +int cppc_get_res_priority(int cpu, int index, u64 *priority) +{ + struct cpc_register_resource *elems; + struct cpc_register_resource *prio_reg; + + if (!priority) + return -EINVAL; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + prio_reg = &elems[PRIORITY_REGISTER]; + if (prio_reg->type != ACPI_TYPE_BUFFER || + IS_NULL_REG(&prio_reg->cpc_entry.reg)) + return -EOPNOTSUPP; + + return cpc_read_reg(cpu, prio_reg, priority); +} +EXPORT_SYMBOL_GPL(cppc_get_res_priority); + +/** + * cppc_set_res_priority - Write priority value for a Resource Priority register. + * @cpu: CPU number. + * @index: Sub-package index. + * @priority: Priority value to write (valid range: [0, PriorityCount - 1]). + * + * Return: 0 on success, negative error otherwise. + */ +int cppc_set_res_priority(int cpu, int index, u64 priority) +{ + struct cpc_register_resource *elems; + struct cpc_register_resource *prio_reg; + u64 prio_count; + int ret; + + elems = get_res_prio_subpkg(cpu, index); + if (!elems) + return -EOPNOTSUPP; + + ret = cpc_read_reg(cpu, &elems[PRIORITY_COUNT], &prio_count); + if (ret) + return ret; + + if (priority >= prio_count) + return -EINVAL; + + prio_reg = &elems[PRIORITY_REGISTER]; + + return cpc_write_reg(cpu, prio_reg, priority); +} +EXPORT_SYMBOL_GPL(cppc_set_res_priority); + /** * cppc_get_transition_latency - returns frequency transition latency in ns * @cpu_num: CPU number for per_cpu(). diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 62cba043b438..483cc3b5f388 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -190,6 +190,14 @@ extern int cppc_get_epp_perf(int cpunum, u64 *epp_perf); extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable); extern int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps); extern int cppc_set_auto_sel(int cpu, bool enable); +extern int cppc_get_resource_priority_count(int cpu, int *count); +extern int cppc_get_resource_priority_resources(int cpu, int index, + u32 *resources, int *num_resources); +extern int cppc_get_res_priority_enable(int cpu, int index, bool *enable); +extern int cppc_set_res_priority_enable(int cpu, int index, bool enable); +extern int cppc_get_res_priority_count(int cpu, int index, u64 *count); +extern int cppc_get_res_priority(int cpu, int index, u64 *priority); +extern int cppc_set_res_priority(int cpu, int index, u64 priority); extern int cppc_get_epp_caps(int cpunum, u64 *epp_val); extern int cppc_set_epp(int cpu, u64 epp_val); extern int cppc_get_auto_act_window(int cpunum, u64 *auto_act_window); @@ -306,6 +314,36 @@ static inline int cppc_set_auto_sel_caps(int cpu, bool enable) { return -EOPNOTSUPP; } +static inline int cppc_get_resource_priority_count(int cpu, int *count) +{ + return -EOPNOTSUPP; + +} +static inline int cppc_get_resource_priority_resources(int cpu, int index, + u32 *resources, int *num_resources) +{ + return -EOPNOTSUPP; +} +static inline int cppc_get_res_priority_enable(int cpu, int index, bool *enable) +{ + return -EOPNOTSUPP; +} +static inline int cppc_set_res_priority_enable(int cpu, int index, bool enable) +{ + return -EOPNOTSUPP; +} +static inline int cppc_get_res_priority_count(int cpu, int index, u64 *count) +{ + return -EOPNOTSUPP; +} +static inline int cppc_get_res_priority(int cpu, int index, u64 *priority) +{ + return -EOPNOTSUPP; +} +static inline int cppc_set_res_priority(int cpu, int index, u64 priority) +{ + return -EOPNOTSUPP; +} #endif /* !CONFIG_ACPI_CPPC_LIB */ -- 2.33.0
driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- Add a "resource_priority" kobject directory under each cpufreq policy that has CPPC v4 Resource Priority entries. Inside it, create a sub-directory per resource priority group (indexed 0..N-1) with the following sysfs attributes: controlled_resources (RO) -- resource type IDs as human-readable names enable (RW) -- enable/disable the priority group priority_count (RO) -- number of priority levels priority (RW) -- current priority value Create the sysfs hierarchy in cppc_cpufreq_cpu_init() and tear it down in cppc_cpufreq_cpu_exit(). A void *res_prio_data pointer in cppc_cpudata tracks the allocation. These attributes allow administrators and power-management daemons to inspect and tune Resource Priority settings at runtime without requiring platform-specific tools. Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++++++++++++++++++++++++- include/acpi/cppc_acpi.h | 1 + 2 files changed, 280 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 2c05c7db48fc..63ebe6c14c3a 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -658,12 +658,15 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) policy->driver_data = NULL; } +static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy); +static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy); + 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; - int ret; + int ret, retval; cpu_data = cppc_cpufreq_get_cpu_data(cpu); if (!cpu_data) { @@ -734,7 +737,19 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) } cppc_cpufreq_cpu_fie_init(policy); - return 0; + + ret = cppc_create_res_prio_sysfs(policy); + if (!ret) + return 0; + + cppc_cpufreq_cpu_fie_exit(policy); + + cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; + + retval = cppc_set_perf(cpu, &cpu_data->perf_ctrls); + if (retval) + pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", + caps->lowest_perf, cpu, retval); out: cppc_cpufreq_put_cpu_data(policy); @@ -748,6 +763,7 @@ static int cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) unsigned int cpu = policy->cpu; int ret; + cppc_remove_res_prio_sysfs(policy); cppc_cpufreq_cpu_fie_exit(policy); cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; @@ -1052,6 +1068,267 @@ static struct freq_attr *cppc_cpufreq_attr[] = { NULL, }; +/* ====== Resource Priority sysfs interface (CPPC v4) ====== */ + +struct cppc_res_prio_group { + struct kobject kobj; + int index; + unsigned int cpu; +}; + +struct cppc_res_prio_data { + struct kobject kobj; + int num_groups; + struct cppc_res_prio_group groups[]; +}; + +static const char * const resource_type_names[] = { + NULL, + "processor_boost", + "processor_throttle", + "l2_cache", + "l3_cache", + "memory_bandwidth", +}; + +#define RESOURCE_TYPE_MAX 5 + +static const char *resource_type_to_name(u32 id) +{ + if (id >= 1 && id <= RESOURCE_TYPE_MAX) + return resource_type_names[id]; + return NULL; +} + +static ssize_t controlled_resources_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u32 resources[32]; + int num = ARRAY_SIZE(resources); + int ret, i, pos = 0; + + ret = cppc_get_resource_priority_resources(grp->cpu, grp->index, + resources, &num); + if (ret) + return ret; + + if (num == 0) + return sysfs_emit(buf, "\n"); + + for (i = 0; i < num; i++) { + const char *name = resource_type_to_name(resources[i]); + + if (name) + pos += sysfs_emit_at(buf, pos, "%s", name); + else + pos += sysfs_emit_at(buf, pos, "unknown(0x%02x)", resources[i]); + + if (i < num - 1) + pos += sysfs_emit_at(buf, pos, " "); + } + + pos += sysfs_emit_at(buf, pos, "\n"); + return pos; +} + +static ssize_t enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + bool val; + int ret; + + ret = cppc_get_res_priority_enable(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t enable_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + ret = cppc_set_res_priority_enable(grp->cpu, grp->index, val); + if (ret) + return ret; + + return count; +} + +static ssize_t priority_count_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = cppc_get_res_priority_count(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu\n", val); +} + +static ssize_t priority_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = cppc_get_res_priority(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu\n", val); +} + +static ssize_t priority_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = kstrtou64(buf, 0, &val); + if (ret) + return ret; + + ret = cppc_set_res_priority(grp->cpu, grp->index, val); + if (ret) + return ret; + + return count; +} + +static struct kobj_attribute attr_controlled_resources = + __ATTR(controlled_resources, 0444, controlled_resources_show, NULL); +static struct kobj_attribute attr_enable = + __ATTR(enable, 0644, enable_show, enable_store); +static struct kobj_attribute attr_priority_count = + __ATTR(priority_count, 0444, priority_count_show, NULL); +static struct kobj_attribute attr_priority = + __ATTR(priority, 0644, priority_show, priority_store); + +static struct attribute *res_prio_group_attrs[] = { + &attr_controlled_resources.attr, + &attr_enable.attr, + &attr_priority_count.attr, + &attr_priority.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(res_prio_group); + +static void cppc_res_prio_group_release(struct kobject *kobj) +{ + /* + * cppc_res_prio_group is embedded in the flexible array of + * cppc_res_prio_data and freed when the parent is released. + */ +} + +static const struct kobj_type cppc_res_prio_group_ktype = { + .release = cppc_res_prio_group_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = res_prio_group_groups, +}; + +static void cppc_res_prio_release(struct kobject *kobj) +{ + struct cppc_res_prio_data *data = + container_of(kobj, struct cppc_res_prio_data, kobj); + + kfree(data); +} + +static const struct kobj_type cppc_res_prio_ktype = { + .release = cppc_res_prio_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + struct cppc_res_prio_data *data = cpu_data->res_prio_data; + int i; + + if (!data) + return; + + for (i = 0; i < data->num_groups; i++) + kobject_put(&data->groups[i].kobj); + + kobject_put(&data->kobj); + cpu_data->res_prio_data = NULL; +} + +static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + struct cppc_res_prio_data *data; + int num, i, ret; + + ret = cppc_get_resource_priority_count(policy->cpu, &num); + if (ret || num <= 0) + return 0; + + data = kzalloc(struct_size(data, groups, num), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num_groups = num; + + ret = kobject_init_and_add(&data->kobj, &cppc_res_prio_ktype, + &policy->kobj, "resource_priority"); + if (ret) { + kobject_put(&data->kobj); + return ret; + } + + for (i = 0; i < num; i++) { + data->groups[i].index = i; + data->groups[i].cpu = policy->cpu; + + ret = kobject_init_and_add(&data->groups[i].kobj, + &cppc_res_prio_group_ktype, + &data->kobj, "%d", i); + if (ret) { + for (; i >= 0; i--) + kobject_put(&data->groups[i].kobj); + kobject_put(&data->kobj); + return ret; + } + } + + cpu_data->res_prio_data = data; + return 0; +} + static struct cpufreq_driver cppc_cpufreq_driver = { .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, .verify = cppc_verify_policy, diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 483cc3b5f388..bb07034a0fdc 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -161,6 +161,7 @@ struct cppc_cpudata { struct cppc_perf_fb_ctrs perf_fb_ctrs; unsigned int shared_type; cpumask_var_t shared_cpu_map; + void *res_prio_data; }; #ifdef CONFIG_ACPI_CPPC_LIB -- 2.33.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/28420 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/U5A... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/28420 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/U5A...
participants (2)
-
Lifeng Zheng -
patchwork bot