@@ -152,6 +152,7 @@ enum rpmi_servicegroup_id {
RPMI_SRVGRP_SYSTEM_RESET = 0x00002,
RPMI_SRVGRP_SYSTEM_SUSPEND = 0x00003,
RPMI_SRVGRP_HSM = 0x00004,
+ RPMI_SRVGRP_CPPC = 0x00005,
RPMI_SRVGRP_ID_MAX_COUNT,
};
@@ -334,4 +335,86 @@ struct rpmi_hsm_get_susp_info_resp {
u32 min_residency_us;
};
+/** RPMI CPPC ServiceGroup Service IDs */
+enum rpmi_cppc_service_id {
+ RPMI_CPPC_SRV_ENABLE_NOTIFICATION = 0x01,
+ RPMI_CPPC_SRV_PROBE_REG = 0x02,
+ RPMI_CPPC_SRV_READ_REG = 0x03,
+ RPMI_CPPC_SRV_WRITE_REG = 0x04,
+ RPMI_CPPC_SRV_GET_FAST_CHANNEL_ADDR = 0x05,
+ RPMI_CPPC_SRV_POKE_FAST_CHANNEL = 0x06,
+ RPMI_CPPC_SRV_GET_HART_LIST = 0x07,
+ RPMI_CPPC_SRV_MAX_COUNT,
+};
+
+struct rpmi_cppc_probe_req {
+ u32 hart_id;
+ u32 reg_id;
+};
+
+struct rpmi_cppc_probe_resp {
+ s32 status;
+ u32 reg_len;
+};
+
+struct rpmi_cppc_read_reg_req {
+ u32 hart_id;
+ u32 reg_id;
+};
+
+struct rpmi_cppc_read_reg_resp {
+ s32 status;
+ u32 data_lo;
+ u32 data_hi;
+};
+
+struct rpmi_cppc_write_reg_req {
+ u32 hart_id;
+ u32 reg_id;
+ u32 data_lo;
+ u32 data_hi;
+};
+
+struct rpmi_cppc_write_reg_resp {
+ s32 status;
+};
+
+struct rpmi_cppc_get_fast_channel_addr_req {
+ u32 hart_id;
+};
+
+struct rpmi_cppc_get_fast_channel_addr_resp {
+ s32 status;
+#define RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_POS 1
+#define RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_MASK \
+ (3U << RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_POS)
+#define RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_SUPPORTED (1U << 0)
+ u32 flags;
+ u32 addr_lo;
+ u32 addr_hi;
+ u32 db_addr_lo;
+ u32 db_addr_hi;
+ u32 db_id_lo;
+ u32 db_id_hi;
+};
+
+enum rpmi_cppc_fast_channel_db_width {
+ RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_8 = 0x0,
+ RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_16 = 0x1,
+ RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_32 = 0x2,
+ RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_64 = 0x3,
+};
+
+struct rpmi_cppc_hart_list_req {
+ u32 start_index;
+};
+
+struct rpmi_cppc_hart_list_resp {
+ s32 status;
+ u32 remaining;
+ u32 returned;
+ /* remaining space need to be adjusted for the above 3 u32's */
+ u32 hartid[(RPMI_MSG_DATA_SIZE(RPMI_SLOT_SIZE_MIN) - (sizeof(u32) * 3)) / sizeof(u32)];
+};
+
#endif /* !__RPMI_MSGPROT_H__ */
@@ -7,4 +7,13 @@ config FDT_CPPC
depends on FDT
default n
+if FDT_CPPC
+
+config FDT_CPPC_RPMI
+ bool "FDT RPMI CPPC driver"
+ depends on FDT_MAILBOX && RPMI_MAILBOX
+ default n
+
+endif
+
endmenu
new file mode 100644
@@ -0,0 +1,287 @@
+/*
+ * 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>
+
+struct rpmi_cppc {
+ struct mbox_chan *chan;
+ bool fast_chan_supported;
+ ulong fast_chan_addr;
+ bool fast_chan_db_supported;
+ enum rpmi_cppc_fast_channel_db_width fast_chan_db_width;
+ ulong fast_chan_db_addr;
+ u64 fast_chan_db_id;
+};
+
+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 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->fast_chan_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 */
+#if __riscv_xlen != 32
+ writeq(val, (void *)cppc->fast_chan_addr);
+#else
+ writel((u32)val, (void *)cppc->fast_chan_addr);
+ writel((u32)(val >> 32), (void *)(cppc->fast_chan_addr + 4));
+#endif
+ if (cppc->fast_chan_db_supported) {
+ switch (cppc->fast_chan_db_width) {
+ case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_8:
+ writeb((u8)cppc->fast_chan_db_id,
+ (void *)cppc->fast_chan_db_addr);
+ break;
+ case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_16:
+ writew((u16)cppc->fast_chan_db_id,
+ (void *)cppc->fast_chan_db_addr);
+ break;
+ case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_32:
+ writel((u32)cppc->fast_chan_db_id,
+ (void *)cppc->fast_chan_db_addr);
+ break;
+ case RPMI_CPPC_FAST_CHANNEL_DB_WIDTH_64:
+#if __riscv_xlen != 32
+ writeq(cppc->fast_chan_db_id,
+ (void *)cppc->fast_chan_db_addr);
+#else
+ writel((u32)cppc->fast_chan_db_id,
+ (void *)cppc->fast_chan_db_addr);
+ writel((u32)(cppc->fast_chan_db_id >> 32),
+ (void *)(cppc->fast_chan_db_addr + 4));
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ 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,
+};
+
+#define TOTAL_FAST_CHAN_SIZE 0x1000
+#define FAST_CHANNEL_REGION_ALIGN 0x1000
+
+static int rpmi_cppc_update_hart_scratch(struct mbox_chan *chan)
+{
+ int rc, i;
+ struct rpmi_cppc_hart_list_req req;
+ struct rpmi_cppc_hart_list_resp resp;
+ struct rpmi_cppc_get_fast_channel_addr_req freq;
+ struct rpmi_cppc_get_fast_channel_addr_resp fresp;
+ struct rpmi_cppc *cppc;
+ unsigned long fast_chan_base_addr;
+
+ /* Workaround for Smepmp issue */
+ freq.hart_id = 0;
+ rc = rpmi_normal_request_with_status(
+ chan, RPMI_CPPC_SRV_GET_FAST_CHANNEL_ADDR,
+ &freq, rpmi_u32_count(freq), rpmi_u32_count(freq),
+ &fresp, rpmi_u32_count(fresp), rpmi_u32_count(fresp));
+ if (rc)
+ return rc;
+
+#if __riscv_xlen == 32
+ fast_chan_base_addr = fresp.addr_lo;
+#else
+ fast_chan_base_addr = (ulong)fresp.addr_hi << 32 |
+ fresp.addr_lo;
+#endif
+ rc = sbi_domain_root_add_memrange(fast_chan_base_addr,
+ TOTAL_FAST_CHAN_SIZE,
+ FAST_CHANNEL_REGION_ALIGN,
+ (SBI_DOMAIN_MEMREGION_MMIO |
+ SBI_DOMAIN_MEMREGION_M_READABLE |
+ SBI_DOMAIN_MEMREGION_M_WRITABLE));
+ if (rc)
+ return rc;
+
+ 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 (i = 0; i < resp.returned; i++) {
+ cppc = rpmi_cppc_get_pointer(resp.hartid[i]);
+ if (!cppc)
+ return SBI_ENOSYS;
+ cppc->chan = chan;
+
+ freq.hart_id = resp.hartid[i];
+ rc = rpmi_normal_request_with_status(
+ chan, RPMI_CPPC_SRV_GET_FAST_CHANNEL_ADDR,
+ &freq, rpmi_u32_count(freq), rpmi_u32_count(freq),
+ &fresp, rpmi_u32_count(fresp), rpmi_u32_count(fresp));
+ if (rc)
+ continue;
+
+ cppc->fast_chan_supported = true;
+#if __riscv_xlen == 32
+ cppc->fast_chan_addr = fresp.addr_lo;
+#else
+ cppc->fast_chan_addr = (ulong)fresp.addr_hi << 32 |
+ fresp.addr_lo;
+#endif
+ cppc->fast_chan_db_supported = fresp.flags &
+ RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_SUPPORTED;
+ cppc->fast_chan_db_width = (fresp.flags &
+ RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_MASK) >>
+ RPMI_CPPC_FAST_CHANNEL_FLAGS_DB_WIDTH_POS;
+#if __riscv_xlen == 32
+ cppc->fast_chan_db_addr = fresp.db_addr_lo;
+#else
+ cppc->fast_chan_db_addr = (ulong)fresp.db_addr_hi << 32 |
+ fresp.db_addr_lo;
+#endif
+ cppc->fast_chan_db_id = (u64)fresp.db_id_hi << 32 |
+ fresp.db_id_lo;
+ }
+
+ req.start_index += resp.returned;
+ } while (resp.remaining);
+
+ return 0;
+}
+
+static int rpmi_cppc_cold_init(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 0;
+
+ /* 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_cppc fdt_cppc_rpmi = {
+ .match_table = rpmi_cppc_match,
+ .cold_init = rpmi_cppc_cold_init,
+};
@@ -9,3 +9,6 @@
libsbiutils-objs-$(CONFIG_FDT_CPPC) += cppc/fdt_cppc.o
libsbiutils-objs-$(CONFIG_FDT_CPPC) += cppc/fdt_cppc_drivers.carray.o
+
+carray-fdt_cppc_drivers-$(CONFIG_FDT_CPPC_RPMI) += fdt_cppc_rpmi
+libsbiutils-objs-$(CONFIG_FDT_CPPC_RPMI) += cppc/fdt_cppc_rpmi.o
@@ -7,6 +7,7 @@ CONFIG_PLATFORM_SOPHGO_SG2042=y
CONFIG_PLATFORM_STARFIVE_JH7110=y
CONFIG_PLATFORM_THEAD=y
CONFIG_FDT_CPPC=y
+CONFIG_FDT_CPPC_RPMI=y
CONFIG_FDT_GPIO=y
CONFIG_FDT_GPIO_DESIGNWARE=y
CONFIG_FDT_GPIO_SIFIVE=y