diff options
author | Subrahmanya Lingappa <slingappa@ventanamicro.com> | 2024-08-07 10:54:01 +0530 |
---|---|---|
committer | Anup Patel <anup@brainfault.org> | 2024-12-06 09:26:47 +0530 |
commit | 591a98bdd549b20504ffda41a41346ad2248ea4d (patch) | |
tree | 34a0c82f9dc0181739377edadd541082a8f46e94 /lib/utils/cppc/fdt_cppc_rpmi.c | |
parent | 54e632b72e80101cf733c47ad1c419d23a8b78e7 (diff) | |
download | opensbi-591a98bdd549b20504ffda41a41346ad2248ea4d.zip opensbi-591a98bdd549b20504ffda41a41346ad2248ea4d.tar.gz opensbi-591a98bdd549b20504ffda41a41346ad2248ea4d.tar.bz2 |
lib: utils/cppc: Add RPMI CPPC driver
Add RPMI based driver for CPPC register read, write and probe.
Signed-off-by: Subrahmanya Lingappa <slingappa@ventanamicro.com>
Co-developed-by: Rahul Pathak <rpathak@ventanamicro.com>
Signed-off-by: Rahul Pathak <rpathak@ventanamicro.com>
Co-developed-by: Sunil V L <sunilvl@ventanamicro.com>
Signed-off-by: Sunil V L <sunilvl@ventanamicro.com>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Diffstat (limited to 'lib/utils/cppc/fdt_cppc_rpmi.c')
-rw-r--r-- | lib/utils/cppc/fdt_cppc_rpmi.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/lib/utils/cppc/fdt_cppc_rpmi.c b/lib/utils/cppc/fdt_cppc_rpmi.c new file mode 100644 index 0000000..26e2d4f --- /dev/null +++ b/lib/utils/cppc/fdt_cppc_rpmi.c @@ -0,0 +1,377 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * Authors: + * Subrahmanya Lingappa <slingappa@ventanamicro.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_cppc.h> +#include <sbi/sbi_ecall_interface.h> +#include <sbi/sbi_scratch.h> +#include <sbi_utils/cppc/fdt_cppc.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/mailbox/fdt_mailbox.h> +#include <sbi_utils/mailbox/rpmi_mailbox.h> + +/** + * Per hart RPMI CPPC fast channel size (bytes) + * PASSIVE MODE: + * 0x0: DESIRED_PERFORMANCE (4-byte) + * 0x4: __RESERVED (4-byte) + * ACTIVE MODE: (not supported yet) + * 0x0: MINIMUM PERFORMANCE (4-byte) + * 0x4: MAXIMUM PERFORMANCE (4-byte) + */ +#define RPMI_CPPC_HART_FASTCHAN_SIZE 0x8 + +struct rpmi_cppc { + struct mbox_chan *chan; + bool fc_supported; + bool fc_db_supported; + enum rpmi_cppc_fast_channel_db_width fc_db_width; + enum rpmi_cppc_fast_channel_cppc_mode mode; + ulong fc_perf_request_addr; + ulong fc_perf_feedback_addr; + ulong fc_db_addr; + u64 fc_db_setmask; + u64 fc_db_preservemask; +}; + +static unsigned long rpmi_cppc_offset; + +static struct rpmi_cppc *rpmi_cppc_get_pointer(u32 hartid) +{ + struct sbi_scratch *scratch; + + scratch = sbi_hartid_to_scratch(hartid); + if (!scratch || !rpmi_cppc_offset) + return NULL; + + return sbi_scratch_offset_ptr(scratch, rpmi_cppc_offset); +} + +static void rpmi_cppc_fc_db_trigger(struct rpmi_cppc *cppc) +{ + u8 db_val_u8 = 0; + u16 db_val_u16 = 0; + u32 db_val_u32 = 0; + + switch (cppc->fc_db_width) { + case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_8: + db_val_u8 = readb((void *)cppc->fc_db_addr); + db_val_u8 = (u8)cppc->fc_db_setmask | + (db_val_u8 & (u8)cppc->fc_db_preservemask); + + writeb(db_val_u8, (void *)cppc->fc_db_addr); + break; + case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_16: + db_val_u16 = readw((void *)cppc->fc_db_addr); + db_val_u16 = (u16)cppc->fc_db_setmask | + (db_val_u16 & (u16)cppc->fc_db_preservemask); + + writew(db_val_u16, (void *)cppc->fc_db_addr); + break; + case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_32: + db_val_u32 = readl((void *)cppc->fc_db_addr); + db_val_u32 = (u32)cppc->fc_db_setmask | + (db_val_u32 & (u32)cppc->fc_db_preservemask); + + writel(db_val_u32, (void *)cppc->fc_db_addr); + break; + case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_64: +#if __riscv_xlen != 32 + u64 db_val_u64 = 0; + db_val_u64 = readq((void *)cppc->fc_db_addr); + db_val_u64 = cppc->fc_db_setmask | + (db_val_u64 & cppc->fc_db_preservemask); + + writeq(db_val_u64, (void *)cppc->fc_db_addr); +#else + u32 db_val_u32_hi = 0; + db_val_u32 = readl((void *)cppc->fc_db_addr); + db_val_u32_hi = readl((void *)(cppc->fc_db_addr + 4)); + + db_val_u32 = (u32)cppc->fc_db_setmask | + (db_val_u32 & (u32)cppc->fc_db_preservemask); + db_val_u32_hi = (u32)(cppc->fc_db_setmask >> 32) | + (db_val_u32 & (u32)(cppc->fc_db_preservemask >> 32)); + + writel(db_val_u32, (void *)cppc->fc_db_addr); + writel(db_val_u32_hi, (void *)(cppc->fc_db_addr + 4)); +#endif + break; + default: + break; + } +} + +static int rpmi_cppc_read(unsigned long reg, u64 *val) +{ + int rc = SBI_SUCCESS; + struct rpmi_cppc_read_reg_req req; + struct rpmi_cppc_read_reg_resp resp; + struct rpmi_cppc *cppc; + + req.hart_id = current_hartid(); + 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 __riscv_xlen == 32 + *val = resp.data_lo; +#else + *val = (u64)resp.data_hi << 32 | resp.data_lo; +#endif + return rc; +} + +static int rpmi_cppc_write(unsigned long reg, u64 val) +{ + int rc = SBI_SUCCESS; + u32 hart_id = current_hartid(); + struct rpmi_cppc_write_reg_req req; + struct rpmi_cppc_write_reg_resp resp; + struct rpmi_cppc *cppc = rpmi_cppc_get_pointer(hart_id); + + if (reg != SBI_CPPC_DESIRED_PERF || !cppc->fc_supported) { + req.hart_id = hart_id; + req.reg_id = reg; + req.data_lo = val & 0xFFFFFFFF; + req.data_hi = val >> 32; + + rc = rpmi_normal_request_with_status( + cppc->chan, RPMI_CPPC_SRV_WRITE_REG, + &req, rpmi_u32_count(req), rpmi_u32_count(req), + &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); + } else { + /* use fast path writes for desired_perf in passive mode */ + writel((u32)val, (void *)cppc->fc_perf_request_addr); + + if (cppc->fc_db_supported) + rpmi_cppc_fc_db_trigger(cppc); + } + + return rc; +} + +static int rpmi_cppc_probe(unsigned long reg) +{ + int rc; + struct rpmi_cppc *cppc; + struct rpmi_cppc_probe_resp resp; + struct rpmi_cppc_probe_req req; + + req.hart_id = current_hartid(); + req.reg_id = reg; + + cppc = rpmi_cppc_get_pointer(req.hart_id); + if (!cppc) + return SBI_ENOSYS; + + rc = rpmi_normal_request_with_status( + cppc->chan, RPMI_CPPC_SRV_PROBE_REG, + &req, rpmi_u32_count(req), rpmi_u32_count(req), + &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); + if (rc) + return rc; + + return resp.reg_len; +} + +static struct sbi_cppc_device sbi_rpmi_cppc = { + .name = "rpmi-cppc", + .cppc_read = rpmi_cppc_read, + .cppc_write = rpmi_cppc_write, + .cppc_probe = rpmi_cppc_probe, +}; + +static int rpmi_cppc_update_hart_scratch(struct mbox_chan *chan) +{ + int rc, i; + bool fc_supported = false; + bool fc_db_supported = false; + struct rpmi_cppc_hart_list_req req; + struct rpmi_cppc_hart_list_resp resp; + struct rpmi_cppc_get_fastchan_offset_req hfreq; + struct rpmi_cppc_get_fastchan_offset_resp hfresp; + struct rpmi_cppc_get_fastchan_region_resp fresp; + enum rpmi_cppc_fast_channel_db_width fc_db_width = 0; + enum rpmi_cppc_fast_channel_cppc_mode cppc_mode = 0; + struct rpmi_cppc *cppc; + unsigned long fc_region_addr = 0; + unsigned long fc_region_size = 0; + unsigned long fc_db_addr = 0; + u64 fc_db_setmask = 0; + u64 fc_db_preservemask = 0; + + rc = rpmi_normal_request_with_status( + chan, RPMI_CPPC_SRV_GET_FAST_CHANNEL_REGION, + NULL, 0, 0, + &fresp, rpmi_u32_count(fresp), rpmi_u32_count(fresp)); + if (rc && rc != SBI_ENOTSUPP) + return rc; + + /* At this point fast channel availability is confirmed */ + fc_supported = (rc != SBI_ENOTSUPP)? true : false; + + /* If fast channel is supported, add the fast channel + * region in root domain as MMIO RW. And, get the doorbell + * information from the response */ + if (fc_supported) { +#if __riscv_xlen == 32 + fc_region_addr = fresp.region_addr_lo; + fc_region_size = fresp.region_size_lo; + fc_db_addr = fresp.db_addr_lo; +#else + fc_region_addr = (ulong)fresp.region_addr_hi << 32 | + fresp.region_addr_lo; + fc_region_size = (ulong)fresp.region_size_hi << 32 | + fresp.region_size_lo; + fc_db_addr = (ulong)fresp.db_addr_hi << 32 | fresp.db_addr_lo; + +#endif + rc = sbi_domain_root_add_memrange(fc_region_addr, + fc_region_size, + RPMI_CPPC_HART_FASTCHAN_SIZE, + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); + if (rc) + return rc; + + cppc_mode = (fresp.flags & RPMI_CPPC_FAST_CHANNEL_CPPC_MODE_MASK) >> + RPMI_CPPC_FAST_CHANNEL_CPPC_MODE_POS; + fc_db_supported = fresp.flags & + RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_SUPPORTED; + fc_db_width = (fresp.flags & + RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_MASK) >> + RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_POS; + fc_db_setmask = (u64)fresp.db_setmask_hi << 32 | + fresp.db_setmask_lo; + fc_db_preservemask = (u64)fresp.db_preservemask_hi << 32 | + fresp.db_preservemask_lo; + } + + /* Get the hart list and depending on the fast channel support + * initialize the per hart cppc structure */ + req.start_index = 0; + do { + rc = rpmi_normal_request_with_status( + chan, RPMI_CPPC_SRV_GET_HART_LIST, + &req, rpmi_u32_count(req), rpmi_u32_count(req), + &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); + if (rc) + return rc; + + /* For each returned harts, get their fastchan offset and + * complete the initialization of per hart cppc structure */ + for (i = 0; i < resp.returned; i++) { + cppc = rpmi_cppc_get_pointer(resp.hartid[i]); + if (!cppc) + return SBI_ENOSYS; + + cppc->chan = chan; + cppc->mode = cppc_mode; + cppc->fc_supported = fc_supported; + + if (fc_supported) { + hfreq.hart_id = resp.hartid[i]; + rc = rpmi_normal_request_with_status( + chan, + RPMI_CPPC_SRV_GET_FAST_CHANNEL_OFFSET, + &hfreq, + rpmi_u32_count(hfreq), + rpmi_u32_count(hfreq), + &hfresp, + rpmi_u32_count(hfresp), + rpmi_u32_count(hfresp)); + if (rc) + continue; + +#if __riscv_xlen == 32 + cppc->fc_perf_request_addr = fc_region_addr + + hfresp.fc_perf_request_offset_lo; + cppc->fc_perf_feedback_addr = fc_region_addr + + hfresp.fc_perf_feedback_offset_lo; +#else + cppc->fc_perf_request_addr = fc_region_addr + + ((ulong)hfresp.fc_perf_request_offset_hi << 32 | + hfresp.fc_perf_request_offset_lo); + + cppc->fc_perf_feedback_addr = fc_region_addr + + ((ulong)hfresp.fc_perf_feedback_offset_hi << 32 | + hfresp.fc_perf_feedback_offset_lo); +#endif + cppc->fc_db_supported = fc_db_supported; + cppc->fc_db_addr = fc_db_addr; + cppc->fc_db_width = fc_db_width; + + cppc->fc_db_setmask = fc_db_setmask; + cppc->fc_db_preservemask = fc_db_preservemask; + } + else { + cppc->fc_perf_request_addr = 0; + cppc->fc_perf_feedback_addr = 0; + cppc->fc_db_supported = 0; + cppc->fc_db_addr = 0; + cppc->fc_db_width = 0; + cppc->fc_db_setmask = 0; + cppc->fc_db_preservemask = 0; + } + } + req.start_index += resp.returned; + } while (resp.remaining); + + return 0; +} + +static int rpmi_cppc_cold_init(const void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct mbox_chan *chan; + + if (!rpmi_cppc_offset) { + rpmi_cppc_offset = + sbi_scratch_alloc_type_offset(struct rpmi_cppc); + if (!rpmi_cppc_offset) + return SBI_ENOMEM; + } + + /* + * If channel request failed then other end does not support + * CPPC service group so do nothing. + */ + rc = fdt_mailbox_request_chan(fdt, nodeoff, 0, &chan); + if (rc) + return SBI_ENODEV; + + /* Update per-HART scratch space */ + rc = rpmi_cppc_update_hart_scratch(chan); + if (rc) + return rc; + + sbi_cppc_set_device(&sbi_rpmi_cppc); + + return 0; +} + +static const struct fdt_match rpmi_cppc_match[] = { + { .compatible = "riscv,rpmi-cppc" }, + {}, +}; + +struct fdt_driver fdt_cppc_rpmi = { + .match_table = rpmi_cppc_match, + .init = rpmi_cppc_cold_init, +}; |