lib/utils/cppc: Emulate REFERENCE_CTR and DELIVERED_CTR locally

Currently, REFERENCE_CTR and DELIVERED_CTR are always read by sending
an RPMI message to the controller. This does not scale well since
these registers are typically polled frequently by CPU frequency
governors.

The reference counter can be simply the TIME csr, so it can be read
locally without going over RPMI. If the fast channel is supported,
the delivered counter can also be emulated locally: the fast channel
exposes the current frequency, which is multiplied by the elapsed
TIME csr ticks since the last read to calculate the delivered
performance counter.

Signed-off-by: Sunil V L <sunilvl@oss.qualcomm.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260703050530.2460037-3-sunilvl@oss.qualcomm.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Sunil V L
2026-08-20 17:57:47 +05:30
committed by Anup Patel
parent 6499c02254
commit bc7a78f41b
+38 -6
View File
@@ -12,6 +12,7 @@
#include <sbi/sbi_cppc.h>
#include <sbi/sbi_ecall_interface.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/mailbox/fdt_mailbox.h>
@@ -39,6 +40,8 @@ struct rpmi_cppc {
ulong fc_db_addr;
u64 fc_db_setmask;
u64 fc_db_preservemask;
u64 prev_time_csr;
u64 prev_delivered_counter;
};
static unsigned long rpmi_cppc_offset;
@@ -107,6 +110,7 @@ static void rpmi_cppc_fc_db_trigger(struct rpmi_cppc *cppc)
static int rpmi_cppc_read(unsigned long reg, u64 *val)
{
u64 curr_freq, curr_time_csr, time_csr_delta, freq;
int rc = SBI_SUCCESS;
struct rpmi_cppc_read_reg_req req;
struct rpmi_cppc_read_reg_resp resp;
@@ -116,12 +120,38 @@ static int rpmi_cppc_read(unsigned long reg, u64 *val)
req.reg_id = reg;
cppc = rpmi_cppc_get_pointer(req.hart_id);
rc = rpmi_normal_request_with_status(
cppc->chan, RPMI_CPPC_SRV_READ_REG,
&req, rpmi_u32_count(req), rpmi_u32_count(req),
&resp, rpmi_u32_count(resp), rpmi_u32_count(resp));
if (rc)
return rc;
if ((reg == SBI_CPPC_DELIVERED_CTR) && cppc->fc_supported) {
#if __riscv_xlen != 32
curr_freq = readq((void *)cppc->fc_perf_feedback_addr);
#else
curr_freq = readl((void *)cppc->fc_perf_feedback_addr + 4);
curr_freq <<= 32;
curr_freq |= readl((void *)cppc->fc_perf_feedback_addr);
#endif
curr_time_csr = sbi_timer_value();
time_csr_delta = curr_time_csr - cppc->prev_time_csr;
cppc->prev_time_csr = curr_time_csr;
freq = sbi_timer_frequency();
if (freq)
cppc->prev_delivered_counter +=
curr_freq * (time_csr_delta / freq) +
(curr_freq * (time_csr_delta % freq)) / freq;
resp.data_lo = cpu_to_le32(cppc->prev_delivered_counter & UINT32_MAX);
resp.data_hi = cpu_to_le32(cppc->prev_delivered_counter >> 32);
} else if (reg == SBI_CPPC_REFERENCE_CTR) {
curr_time_csr = sbi_timer_value();
resp.data_lo = cpu_to_le32(curr_time_csr & UINT32_MAX);
resp.data_hi = cpu_to_le32(curr_time_csr >> 32);
} else {
rc = rpmi_normal_request_with_status(
cppc->chan, RPMI_CPPC_SRV_READ_REG,
&req, rpmi_u32_count(req), rpmi_u32_count(req),
&resp, rpmi_u32_count(resp), rpmi_u32_count(resp));
if (rc)
return rc;
}
#if __riscv_xlen == 32
*val = resp.data_lo;
@@ -276,6 +306,8 @@ static int rpmi_cppc_update_hart_scratch(struct mbox_chan *chan)
if (!cppc)
return SBI_ENOSYS;
cppc->prev_time_csr = 0;
cppc->prev_delivered_counter = 0;
cppc->chan = chan;
cppc->mode = cppc_mode;
cppc->fc_supported = fc_supported;