ACPI: CPPC: Validate and access PCC register layouts

PCC GAS Access Size identifies the subspace while Bit Width identifies
the field size. Support zero-offset byte-multiple fields up to 64 bits
with byte-oriented I/O and explicit little-endian encoding. This accepts
the unaligned 32-bit Delivered Performance Counter in the ACPI
implementation example and read-only widths such as 24 bits. A
per-subspace raw spinlock serializes aliased payload copies made under
the shared side of pcc_lock.

Defer unsupported-field decisions until the package is known. A provided
but inaccessible CPPC Enable or Autonomous Selection Enable control
fails probe. OSPM must set the former and cannot discard the latter
without knowing the hardware autonomous-mode state. An inaccessible
Desired control can be ignored only for immutable autonomous operation.
If both Minimum and Maximum controls are inaccessible, disable the pair
coherently; a one-sided bound fails probe. Other inaccessible optional
controls are marked absent.

Validate PCC ranges after the eight-byte header and reject overlapping
logical ranges when either is writable. Bit-level PCC RMW remains
unsupported.

Probe now rejects or disables unsupported writable PCC widths before
publishing the descriptor. Remove the earlier setter preflights which
repeat that validation.

Fixes: 85b1407bf6 ("ACPI / CPPC: Make CPPC ACPI driver aware of PCC subspace IDs")
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
Tested-by: Sumit Gupta <sumitg@nvidia.com>
Link: https://patch.msgid.link/20260916162805.1039247-11-christian.loehle@arm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Christian Loehle 2026-09-16 17:27:55 +01:00 committed by Rafael J. Wysocki
parent 09d911c996
commit fd1bb9a6cb

View File

@ -74,6 +74,8 @@ struct cppc_pcc_data {
* Take write_lock for all purposes which gives exclusive access
*/
struct rw_semaphore pcc_lock;
/* Serialize byte-oriented accesses to aliased PCC payload fields. */
raw_spinlock_t payload_lock;
/* Wait queue for CPUs whose requests were batched */
wait_queue_head_t pcc_write_wait_q;
@ -132,9 +134,11 @@ static struct cpc_sysmem_node *cpc_sysmem_next(struct cpc_sysmem_node *node,
return cpc_sysmem_itree_iter_next(node, start, last);
}
#define CPC_PCC_HEADER_SIZE 0x8
/* pcc mapped address + header size + offset within PCC subspace */
#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_channel->shmem + \
0x8 + (offs))
CPC_PCC_HEADER_SIZE + (offs))
/* Check if a CPC register is in PCC */
#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
@ -286,19 +290,6 @@ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
(reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \
(8 << ((reg)->access_width - 1)) : (reg)->bit_width)
static bool cpc_pcc_write_supported(const struct cpc_register_resource *reg)
{
switch (GET_BIT_WIDTH(&reg->cpc_entry.reg)) {
case 8:
case 16:
case 32:
case 64:
return true;
default:
return false;
}
}
/* Shift and apply the mask for CPC reads/writes */
#define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \
GENMASK(((reg)->bit_width) - 1, 0))
@ -472,6 +463,196 @@ static int cpc_validate_sysmem_reg(struct cpc_desc *cpc_desc,
return -EINVAL;
}
static bool cpc_immutable_autonomous(const struct cpc_desc *cpc_desc)
{
const struct cpc_register_resource *reg;
reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
return osc_sb_cppc2_support_acked && reg->type == ACPI_TYPE_INTEGER &&
reg->cpc_entry.int_value == 1;
}
static int cpc_resolve_unsupported(struct cpc_desc *cpc_desc,
u32 unsupported)
{
unsigned int i;
u32 bounds = BIT(MIN_PERF) | BIT(MAX_PERF);
bool min_unusable, max_unusable;
if (unsupported & bounds) {
min_unusable = (unsupported & BIT(MIN_PERF)) ||
!cpc_is_writable(&cpc_desc->cpc_regs[MIN_PERF]);
max_unusable = (unsupported & BIT(MAX_PERF)) ||
!cpc_is_writable(&cpc_desc->cpc_regs[MAX_PERF]);
if (min_unusable && max_unusable) {
pr_warn("CPU%d: ignoring inaccessible Minimum and Maximum Performance registers\n",
cpc_desc->cpu_id);
cpc_disable_reg(cpc_desc, MIN_PERF);
cpc_disable_reg(cpc_desc, MAX_PERF);
unsupported &= ~bounds;
}
}
for (i = 0; i < cpc_desc->num_entries - 2; i++) {
if (!(unsupported & BIT(i)))
continue;
if (i == DESIRED_PERF && cpc_immutable_autonomous(cpc_desc)) {
pr_warn("CPU%d: ignoring inaccessible Desired Performance register in autonomous mode\n",
cpc_desc->cpu_id);
cpc_disable_reg(cpc_desc, i);
continue;
}
/*
* A present Enable or Autonomous Selection control must remain
* usable. Disabling the latter could leave autonomous selection
* enabled while OSPM believes that it has disabled it.
*/
if (i == ENABLE ||
(i == AUTO_SEL_ENABLE && cpc_entry_present(&cpc_desc->cpc_regs[i])) ||
i == MIN_PERF || i == MAX_PERF ||
!IS_OPTIONAL_CPC_REG(i)) {
pr_err("CPU%d: cannot access _CPC register %u\n",
cpc_desc->cpu_id, i);
return -EINVAL;
}
pr_warn("CPU%d: ignoring inaccessible optional _CPC register %u\n",
cpc_desc->cpu_id, i);
cpc_disable_reg(cpc_desc, i);
}
return 0;
}
static int cpc_validate_required_controls(struct cpc_desc *cpc_desc)
{
unsigned int i;
/*
* Performance Limited is required by the specification, but tolerate a
* NULL descriptor used by firmware which cannot report limiting events.
* CPPC control does not depend on this status.
*/
for (i = 0; i < cpc_desc->num_entries - 2; i++) {
if (i != DESIRED_PERF && i != PERF_LIMITED &&
!IS_OPTIONAL_CPC_REG(i) &&
!cpc_entry_present(&cpc_desc->cpc_regs[i])) {
pr_debug("CPU:%d lacks mandatory _CPC register %u\n",
cpc_desc->cpu_id, i);
return -EINVAL;
}
}
/* Desired may be absent only for immutable autonomous operation. */
if (!cpc_is_writable(&cpc_desc->cpc_regs[DESIRED_PERF]) &&
!cpc_immutable_autonomous(cpc_desc)) {
pr_debug("CPU:%d lacks a writable Desired Performance register\n",
cpc_desc->cpu_id);
return -EINVAL;
}
return 0;
}
static int cpc_validate_bound_controls(struct cpc_desc *cpc_desc)
{
bool have_min, have_max;
have_min = cpc_is_writable(&cpc_desc->cpc_regs[MIN_PERF]);
have_max = cpc_is_writable(&cpc_desc->cpc_regs[MAX_PERF]);
if (have_min != have_max) {
pr_err("CPU%d: _CPC must provide both Minimum and Maximum Performance or neither\n",
cpc_desc->cpu_id);
return -EINVAL;
}
return 0;
}
static void cpc_validate_pcc_bounds(struct cpc_desc *cpc_desc,
struct cppc_pcc_data *data,
u32 *unsupported)
{
u64 shmem_size = data->pcc_channel->shmem_size;
unsigned int i;
for (i = 0; i < cpc_desc->num_entries - 2; i++) {
struct cpc_register_resource *reg = &cpc_desc->cpc_regs[i];
struct cpc_reg *gas;
u64 access_size;
if ((*unsupported & BIT(i)) || !CPC_SUPPORTED(reg) ||
!CPC_IN_PCC(reg))
continue;
gas = &reg->cpc_entry.reg;
access_size = gas->bit_width / 8;
if (shmem_size >= CPC_PCC_HEADER_SIZE &&
gas->address <= shmem_size - CPC_PCC_HEADER_SIZE &&
access_size <= shmem_size - CPC_PCC_HEADER_SIZE - gas->address)
continue;
pr_debug("CPU%d: _CPC register %u exceeds the PCC shared region\n",
cpc_desc->cpu_id, i);
*unsupported |= BIT(i);
}
}
static u64 cpc_non_mmio_access_size(const struct cpc_register_resource *reg)
{
const struct cpc_reg *gas = &reg->cpc_entry.reg;
if (gas->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
return gas->bit_width / 8;
return cpc_reg_access_width(gas) / 8;
}
static int cpc_validate_non_mmio_overlaps(struct cpc_desc *cpc_desc,
u8 space_id, const char *name)
{
unsigned int i, j;
for (i = 0; i < cpc_desc->num_entries - 2; i++) {
struct cpc_register_resource *a = &cpc_desc->cpc_regs[i];
const struct cpc_reg *a_gas;
u64 a_size;
if (!CPC_SUPPORTED(a) || a->type != ACPI_TYPE_BUFFER ||
a->cpc_entry.reg.space_id != space_id)
continue;
a_gas = &a->cpc_entry.reg;
a_size = cpc_non_mmio_access_size(a);
for (j = i + 1; j < cpc_desc->num_entries - 2; j++) {
struct cpc_register_resource *b = &cpc_desc->cpc_regs[j];
const struct cpc_reg *b_gas;
u64 b_size;
if (!CPC_SUPPORTED(b) || b->type != ACPI_TYPE_BUFFER ||
b->cpc_entry.reg.space_id != space_id)
continue;
b_gas = &b->cpc_entry.reg;
b_size = cpc_non_mmio_access_size(b);
if (!cpc_reg_is_writable(i) && !cpc_reg_is_writable(j))
continue;
if (a_gas->address < b_gas->address ?
b_gas->address - a_gas->address >= a_size :
a_gas->address - b_gas->address >= b_size)
continue;
pr_err("CPU%d: overlapping writable %s _CPC registers %u and %u\n",
cpc_desc->cpu_id, name, i, j);
return -EINVAL;
}
}
return 0;
}
static void cpc_mark_rmw_lock_users(struct cpc_desc *cpc_desc)
{
int i;
@ -847,6 +1028,8 @@ static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
pcc_ss_data->deadline_us);
if (likely(!ret)) {
/* Order completion status before reading the returned payload. */
rmb();
pcc_ss_data->platform_owns_pcc = false;
if (chk_err_bit && (status & PCC_ERROR_MASK))
ret = -EIO;
@ -1290,6 +1473,7 @@ static int pcc_data_alloc(int pcc_ss_id)
ret = -ENOMEM;
goto out_unlock;
}
raw_spin_lock_init(&data->payload_lock);
pcc_data[pcc_ss_id] = data;
}
data->refcount++;
@ -1365,11 +1549,12 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
struct device *cpu_dev;
acpi_handle handle = pr->handle;
unsigned int num_ent, i, cpc_rev;
u32 unsupported_regs = 0;
int pcc_subspace_id = -1;
bool pcc_data_ref = false;
bool cpc_present = false;
acpi_status status;
int ret = -ENODATA;
int ret = -EINVAL;
int err;
if (per_cpu(cpc_desc_ptr, pr->id))
@ -1519,6 +1704,24 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
* so extract it only once.
*/
if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
/* These registers have no specified 32-bit upper bound. */
bool wide_write = i - 2 == PERF_LIMITED ||
i - 2 == ENABLE ||
i - 2 == AUTO_SEL_ENABLE;
bool write_width_supported = gas_t->bit_width == 8 ||
gas_t->bit_width == 16 ||
gas_t->bit_width == 32 ||
gas_t->bit_width == 64;
if (!gas_t->bit_width || gas_t->bit_width > 64 ||
gas_t->bit_offset || gas_t->bit_width % 8 ||
(cpc_reg_is_writable(i - 2) &&
(!write_width_supported ||
(!wide_write && gas_t->bit_width > 32)))) {
unsupported_regs |= BIT(i - 2);
continue;
}
if (pcc_subspace_id < 0) {
pcc_subspace_id = gas_t->access_width;
err = pcc_data_alloc(pcc_subspace_id);
@ -1530,6 +1733,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
} else if (pcc_subspace_id != gas_t->access_width) {
pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
pr->id);
ret = -EINVAL;
goto out_free;
}
} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
@ -1537,21 +1741,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
void __iomem *addr;
size_t access_width;
err = cpc_validate_sysmem_reg(cpc_ptr, gas_t, i - 2);
if (err && (i - 2 == DESIRED_PERF ||
i - 2 == OSPM_NOMINAL_PERF)) {
const char *name = i - 2 == DESIRED_PERF ?
"Desired Performance" :
"OSPM Nominal Performance";
pr_warn("CPU%d: disabling inaccessible %s register\n",
pr->id, name);
cpc_disable_reg(cpc_ptr, i - 2);
continue;
}
err = cpc_validate_sysmem_reg(cpc_ptr, gas_t,
i - 2);
if (err) {
ret = err;
goto out_free;
unsupported_regs |= BIT(i - 2);
continue;
}
if (!cpc_is_readable(&cpc_ptr->cpc_regs[i - 2]) &&
!cpc_is_writable(&cpc_ptr->cpc_regs[i - 2]))
@ -1625,35 +1819,14 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
}
per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
/*
* Performance Limited is required by the specification, but tolerate a
* NULL descriptor used by firmware which cannot report limiting events.
* CPPC control does not depend on this status.
*/
for (i = 0; i < num_ent - 2; i++) {
if (i != DESIRED_PERF && i != PERF_LIMITED &&
!IS_OPTIONAL_CPC_REG(i) &&
!cpc_entry_present(&cpc_ptr->cpc_regs[i])) {
pr_debug("CPU:%d lacks mandatory _CPC register %u\n",
pr->id, i);
ret = -EINVAL;
goto out_free;
}
}
/*
* In CPPC v1, DESIRED_PERF is mandatory. In CPPC v2, it is optional
* only when AUTO_SEL_ENABLE is supported.
*/
if (!cpc_is_writable(&cpc_ptr->cpc_regs[DESIRED_PERF]) &&
(!osc_sb_cppc2_support_acked ||
cpc_ptr->cpc_regs[AUTO_SEL_ENABLE].type != ACPI_TYPE_INTEGER ||
cpc_ptr->cpc_regs[AUTO_SEL_ENABLE].cpc_entry.int_value != 1)) {
pr_debug("CPU:%d lacks a writable Desired Performance register\n",
pr->id);
ret = -EINVAL;
ret = cpc_resolve_unsupported(cpc_ptr, unsupported_regs);
if (ret)
goto out_free;
unsupported_regs = 0;
ret = cpc_validate_required_controls(cpc_ptr);
if (ret)
goto out_free;
}
/*
* Initialize the remaining cpc_regs as unsupported.
@ -1684,6 +1857,24 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
if (ret)
goto out_free;
}
if (pcc_subspace_id >= 0) {
cpc_validate_pcc_bounds(cpc_ptr, pcc_data[pcc_subspace_id],
&unsupported_regs);
ret = cpc_resolve_unsupported(cpc_ptr, unsupported_regs);
if (ret)
goto out_free;
}
ret = cpc_validate_non_mmio_overlaps(cpc_ptr,
ACPI_ADR_SPACE_PLATFORM_COMM,
"PCC");
if (ret)
goto out_free;
ret = cpc_validate_bound_controls(cpc_ptr);
if (ret)
goto out_free;
/* Everything looks okay */
pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
@ -1810,6 +2001,9 @@ int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
{
void __iomem *vaddr = NULL;
unsigned long flags;
u8 buf[sizeof(*val)];
unsigned int i;
int size;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
@ -1837,15 +2031,30 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
*val = val_u32;
return 0;
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0) {
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
if (pcc_ss_id < 0 || !pcc_data[pcc_ss_id])
return -ENODEV;
/*
* For registers in PCC space, the register size is determined
* by the bit width field; the access size is used to indicate
* the PCC subspace id.
*/
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
}
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
size = reg->bit_width / 8;
if (!size || size > sizeof(buf) || reg->bit_width % 8)
return -EFAULT;
raw_spin_lock_irqsave(&pcc_data[pcc_ss_id]->payload_lock, flags);
memcpy_fromio(buf, vaddr, size);
raw_spin_unlock_irqrestore(&pcc_data[pcc_ss_id]->payload_lock,
flags);
*val = 0;
for (i = 0; i < size; i++)
*val |= (u64)buf[i] << (i * 8);
return 0;
} else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_read_ffh(cpu, reg, val);
@ -1867,18 +2076,12 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
*val = readq_relaxed(vaddr);
break;
default:
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
pr_debug("Error: Cannot read %u bit width from system memory: 0x%llx\n",
size, reg->address);
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
pr_debug("Error: Cannot read %u bit width from PCC for ss: %d\n",
size, pcc_ss_id);
}
pr_debug("Error: Cannot read %u bit width from system memory: 0x%llx\n",
size, reg->address);
return -EFAULT;
}
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
*val = MASK_VAL_READ(reg, *val);
*val = MASK_VAL_READ(reg, *val);
return 0;
}
@ -1893,6 +2096,8 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
struct cpc_reg *reg;
struct cpc_desc *cpc_desc;
unsigned long flags;
u8 buf[sizeof(val)];
unsigned int i;
bool locked = false;
if (reg_res->type != ACPI_TYPE_BUFFER)
@ -1917,15 +2122,31 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
}
return 0;
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM && pcc_ss_id >= 0) {
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
if (pcc_ss_id < 0 || !pcc_data[pcc_ss_id])
return -ENODEV;
/*
* For registers in PCC space, the register size is determined
* by the bit width field; the access size is used to indicate
* the PCC subspace id.
*/
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
}
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
size = reg->bit_width / 8;
if (!size || size > sizeof(buf) || reg->bit_width % 8)
return -EFAULT;
for (i = 0; i < size; i++)
buf[i] = val >> (i * 8);
raw_spin_lock_irqsave(&pcc_data[pcc_ss_id]->payload_lock, flags);
memcpy_toio(vaddr, buf, size);
/* Publish every payload byte before another CPU can ring the doorbell. */
wmb();
raw_spin_unlock_irqrestore(&pcc_data[pcc_ss_id]->payload_lock,
flags);
return 0;
} else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_write_ffh(cpu, reg, val);
@ -1933,44 +2154,42 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
return acpi_os_write_memory((acpi_physical_address)reg->address,
val, size);
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
/* Partial fields and local overlaps use the descriptor lock. */
locked = reg_res->cpc_entry.use_rmw_lock;
if (locked) {
cpc_desc = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpu);
return -ENODEV;
}
raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
/* Partial fields and local overlaps use the descriptor lock. */
locked = reg_res->cpc_entry.use_rmw_lock;
if (locked) {
cpc_desc = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpu);
return -ENODEV;
}
raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
}
if (reg->bit_offset || reg->bit_width != size) {
/*
* MASK_VAL_WRITE() discards the field's old bits, so undefined
* readback from a write-only field is not propagated.
*/
switch (size) {
case 8:
prev_val = readb_relaxed(vaddr);
break;
case 16:
prev_val = readw_relaxed(vaddr);
break;
case 32:
prev_val = readl_relaxed(vaddr);
break;
case 64:
prev_val = readq_relaxed(vaddr);
break;
default:
if (locked)
raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
flags);
return -EFAULT;
}
val = MASK_VAL_WRITE(reg, prev_val, val);
if (reg->bit_offset || reg->bit_width != size) {
/*
* MASK_VAL_WRITE() discards the field's old bits, so undefined
* readback from a write-only field is not propagated.
*/
switch (size) {
case 8:
prev_val = readb_relaxed(vaddr);
break;
case 16:
prev_val = readw_relaxed(vaddr);
break;
case 32:
prev_val = readl_relaxed(vaddr);
break;
case 64:
prev_val = readq_relaxed(vaddr);
break;
default:
if (locked)
raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
flags);
return -EFAULT;
}
val = MASK_VAL_WRITE(reg, prev_val, val);
}
switch (size) {
@ -1987,13 +2206,8 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
writeq_relaxed(val, vaddr);
break;
default:
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
pr_debug("Error: Cannot write %u bit width to system memory: 0x%llx\n",
size, reg->address);
} else if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
pr_debug("Error: Cannot write %u bit width to PCC for ss: %d\n",
size, pcc_ss_id);
}
pr_debug("Error: Cannot write %u bit width to system memory: 0x%llx\n",
size, reg->address);
ret_val = -EFAULT;
break;
}
@ -2074,9 +2288,6 @@ static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
}
if (!cpc_pcc_write_supported(reg))
return -EFAULT;
pcc_ss_data = pcc_data[pcc_ss_id];
if (!pcc_ss_data)
return -ENODEV;
@ -2490,9 +2701,6 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
auto_sel_pcc = cpc_is_writable(auto_sel_reg) &&
CPC_IN_PCC(auto_sel_reg);
epp_pcc = cpc_is_writable(epp_set_reg) && CPC_IN_PCC(epp_set_reg);
if ((auto_sel_pcc && !cpc_pcc_write_supported(auto_sel_reg)) ||
(epp_pcc && !cpc_pcc_write_supported(epp_set_reg)))
return -EFAULT;
if ((cpc_is_writable(auto_sel_reg) &&
CPC_IN_SYSTEM_IO(auto_sel_reg) &&
!cpc_system_io_write_supported(auto_sel_reg)) ||
@ -2838,12 +3046,6 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
!CPC_IN_PCC(max_perf_reg));
mixed_layout = pcc_layout && direct_layout;
/* Do not modify any control if a requested PCC field cannot be staged. */
if ((desired_pcc && !cpc_pcc_write_supported(desired_reg)) ||
(min_pcc && !cpc_pcc_write_supported(min_perf_reg)) ||
(max_pcc && !cpc_pcc_write_supported(max_perf_reg)))
return -EFAULT;
if (mixed_layout || pcc_update) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");