diff options
Diffstat (limited to 'drivers/acpi/cppc_acpi.c')
-rw-r--r-- | drivers/acpi/cppc_acpi.c | 329 |
1 files changed, 193 insertions, 136 deletions
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f193e713825a..a9ae2fd62863 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -47,7 +47,6 @@ struct cppc_pcc_data { struct pcc_mbox_chan *pcc_channel; - void __iomem *pcc_comm_addr; bool pcc_channel_acquired; unsigned int deadline_us; unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; @@ -95,7 +94,7 @@ static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx); static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); /* pcc mapped address + header size + offset within PCC subspace */ -#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \ +#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_channel->shmem + \ 0x8 + (offs)) /* Check if a CPC register is in PCC */ @@ -129,6 +128,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 @@ -223,7 +236,7 @@ static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit) int ret, status; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = - pcc_ss_data->pcc_comm_addr; + pcc_ss_data->pcc_channel->shmem; if (!pcc_ss_data->platform_owns_pcc) return 0; @@ -258,7 +271,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) int ret = -EIO, i; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = - pcc_ss_data->pcc_comm_addr; + pcc_ss_data->pcc_channel->shmem; unsigned int time_delta; /* @@ -571,15 +584,6 @@ static int register_pcc_channel(int pcc_ss_idx) pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate; pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency; - pcc_data[pcc_ss_idx]->pcc_comm_addr = - acpi_os_ioremap(pcc_chan->shmem_base_addr, - pcc_chan->shmem_size); - if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) { - pr_err("Failed to ioremap PCC comm region mem for %d\n", - pcc_ss_idx); - return -ENOMEM; - } - /* Set flag so that we don't come here for each CPU. */ pcc_data[pcc_ss_idx]->pcc_channel_acquired = true; } @@ -1175,43 +1179,106 @@ 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_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + 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); struct cpc_register_resource *reg; + 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; } reg = &cpc_desc->cpc_regs[reg_idx]; - 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; + 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 (pcc_ss_id < 0) - return -EIO; + if (CPC_IN_PCC(reg)) + return cppc_get_reg_val_in_pcc(cpu, reg, val); - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); + return cpc_read(cpu, reg, val); +} - if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - cpc_read(cpunum, reg, perf); - else - ret = -EIO; +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; - up_write(&pcc_ss_data->pcc_lock); + 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; } - cpc_read(cpunum, reg, perf); + reg = &cpc_desc->cpc_regs[reg_idx]; - return 0; + /* 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); } /** @@ -1223,7 +1290,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) */ int cppc_get_desired_perf(int cpunum, u64 *desired_perf) { - 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); @@ -1236,7 +1303,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); } /** @@ -1248,7 +1315,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); @@ -1261,7 +1328,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); @@ -1535,53 +1602,110 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable) EXPORT_SYMBOL_GPL(cppc_set_epp_perf); /** - * cppc_get_auto_sel_caps - Read autonomous selection register. - * @cpunum : CPU from which to read register. - * @perf_caps : struct where autonomous selection register value is updated. + * cppc_set_epp() - Write the EPP register. + * @cpu: CPU on which to write register. + * @epp_val: Value to write to the EPP register. */ -int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps) +int cppc_set_epp(int cpu, u64 epp_val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); - struct cpc_register_resource *auto_sel_reg; - u64 auto_sel; + if (epp_val > CPPC_ENERGY_PERF_MAX) + return -EINVAL; - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpunum); - return -ENODEV; - } + return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val); +} +EXPORT_SYMBOL_GPL(cppc_set_epp); - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; +/** + * cppc_get_auto_act_window() - Read autonomous activity window register. + * @cpu: CPU from which to read register. + * @auto_act_window: Return address. + * + * According to ACPI 6.5, s8.4.6.1.6, the value read from the autonomous + * activity window register consists of two parts: a 7 bits value indicate + * significand and a 3 bits value indicate exponent. + */ +int cppc_get_auto_act_window(int cpu, u64 *auto_act_window) +{ + unsigned int exp; + u64 val, sig; + int ret; - if (!CPC_SUPPORTED(auto_sel_reg)) - pr_warn_once("Autonomous mode is not unsupported!\n"); + if (auto_act_window == NULL) + return -EINVAL; - 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; + ret = cppc_get_reg_val(cpu, AUTO_ACT_WINDOW, &val); + if (ret) + return ret; - if (pcc_ss_id < 0) - return -ENODEV; + sig = val & CPPC_AUTO_ACT_WINDOW_MAX_SIG; + exp = (val >> CPPC_AUTO_ACT_WINDOW_SIG_BIT_SIZE) & CPPC_AUTO_ACT_WINDOW_MAX_EXP; + *auto_act_window = sig * int_pow(10, exp); - pcc_ss_data = pcc_data[pcc_ss_id]; + return 0; +} +EXPORT_SYMBOL_GPL(cppc_get_auto_act_window); - down_write(&pcc_ss_data->pcc_lock); +/** + * cppc_set_auto_act_window() - Write autonomous activity window register. + * @cpu: CPU on which to write register. + * @auto_act_window: usec value to write to the autonomous activity window register. + * + * According to ACPI 6.5, s8.4.6.1.6, the value to write to the autonomous + * activity window register consists of two parts: a 7 bits value indicate + * significand and a 3 bits value indicate exponent. + */ +int cppc_set_auto_act_window(int cpu, u64 auto_act_window) +{ + /* The max value to store is 1270000000 */ + u64 max_val = CPPC_AUTO_ACT_WINDOW_MAX_SIG * int_pow(10, CPPC_AUTO_ACT_WINDOW_MAX_EXP); + int exp = 0; + u64 val; - 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; - } + if (auto_act_window > max_val) + return -EINVAL; - up_write(&pcc_ss_data->pcc_lock); + /* + * The max significand is 127, when auto_act_window is larger than + * 129, discard the precision of the last digit and increase the + * exponent by 1. + */ + while (auto_act_window > CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH) { + auto_act_window /= 10; + exp += 1; + } + + /* For 128 and 129, cut it to 127. */ + if (auto_act_window > CPPC_AUTO_ACT_WINDOW_MAX_SIG) + auto_act_window = CPPC_AUTO_ACT_WINDOW_MAX_SIG; + + val = (exp << CPPC_AUTO_ACT_WINDOW_SIG_BIT_SIZE) + auto_act_window; + return cppc_set_reg_val(cpu, AUTO_ACT_WINDOW, val); +} +EXPORT_SYMBOL_GPL(cppc_set_auto_act_window); + +/** + * cppc_get_auto_sel() - Read autonomous selection register. + * @cpu: CPU from which to read register. + * @enable: Return address. + */ +int cppc_get_auto_sel(int cpu, bool *enable) +{ + u64 auto_sel; + int ret; + + if (enable == NULL) + return -EINVAL; + + ret = cppc_get_reg_val(cpu, AUTO_SEL_ENABLE, &auto_sel); + if (ret) return ret; - } + + *enable = (bool)auto_sel; return 0; } -EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps); +EXPORT_SYMBOL_GPL(cppc_get_auto_sel); /** * cppc_set_auto_sel - Write autonomous selection register. @@ -1590,43 +1714,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); @@ -1640,38 +1728,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); |