ACPI: CPPC: Avoid unnecessary reads for full-width writes

SystemMemory GAS entries may describe a field within a wider access
unit, so cpc_write() reads the access unit before updating the field to
preserve the surrounding bits. It also does this when the field covers
the complete access unit.

When the bit offset is zero and the register bit width equals the
resolved access width, the previous value cannot affect the result. Skip
the MMIO read and mask operation in that case. Retain rmw_lock because
another entry in the same _CPC package may share the access unit.

Signed-off-by: Christian Loehle <christian.loehle@arm.com>
Reviewed-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
Link: https://patch.msgid.link/20260803210527.1285229-2-christian.loehle@arm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Christian Loehle 2026-08-03 22:05:25 +01:00 committed by Rafael J. Wysocki
parent 334dd0ed87
commit 9d9157ee90

View File

@ -1162,25 +1162,34 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
return -ENODEV;
}
/*
* Only partial fields need the previous contents to preserve bits
* outside the field. Keep serializing full-width writes because
* another _CPC entry may share the access unit and require RMW.
*/
raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
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:
raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
return -EFAULT;
if (reg->bit_offset || reg->bit_width != size) {
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:
raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
flags);
return -EFAULT;
}
val = MASK_VAL_WRITE(reg, prev_val, val);
}
val = MASK_VAL_WRITE(reg, prev_val, val);
}
switch (size) {