diff mbox series

[v2,07/11] lib: utils: fdt_fixup: Add fdt_add_pmu_mappings() helper function

Message ID 20231019113713.3508153-8-peterlin@andestech.com
State Changes Requested
Headers show
Series Add Andes PMU extension support | expand

Commit Message

Yu-Chien Peter Lin Oct. 19, 2023, 11:37 a.m. UTC
Add fdt_add_pmu_mappings() that creates entries of riscv,*event-to-mhpm*
property from arrays right before fdt_pmu_setup() populating the mapping
tables (i.e. hw_event_map[] and fdt_pmu_evt_select[]).

The helper function will skip the creation of those properties if a
"/pmu" node with "riscv,pmu" compatible string is provided.

Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
---
Changes v1 -> v2:
  - New patch
---
 include/sbi_utils/fdt/fdt_fixup.h            | 48 ++++++++++
 lib/utils/fdt/fdt_fixup.c                    | 95 ++++++++++++++++++++
 platform/generic/include/platform_override.h |  1 +
 platform/generic/platform.c                  | 10 ++-
 4 files changed, 153 insertions(+), 1 deletion(-)

Comments

Inochi Amaoto Oct. 21, 2023, 12:25 p.m. UTC | #1
>Implement the andes_fdt_add_pmu_mappings() callback, which creates PMU
>node properties that will later be populated with mapping tables.
>
>Currently we only support 45-series event mappings.
>
>Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
>---
>Changes v1 -> v2:
>  - New patch
>---
> include/sbi/sbi_ecall_interface.h          |   5 +
> platform/generic/Kconfig                   |   2 +
> platform/generic/andes/Kconfig             |   7 +
> platform/generic/andes/ae350.c             |   2 +
> platform/generic/andes/andes_hpm.c         | 381 +++++++++++++++++++++
> platform/generic/andes/objects.mk          |   1 +
> platform/generic/include/andes/andes_hpm.h |  83 +++++
> platform/generic/renesas/rzfive/rzfive.c   |   2 +
> 8 files changed, 483 insertions(+)
> create mode 100644 platform/generic/andes/andes_hpm.c
> create mode 100644 platform/generic/include/andes/andes_hpm.h
>
>diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h
>index 1fe469e..89187e7 100644
>--- a/include/sbi/sbi_ecall_interface.h
>+++ b/include/sbi/sbi_ecall_interface.h
>@@ -155,6 +155,11 @@ enum sbi_pmu_hw_cache_op_result_id {
> 	SBI_PMU_HW_CACHE_RESULT_MAX,
> };
>
>+#define SBI_PMU_HW_CACHE_EVENT_IDX(id, op, res) \
>+	(SBI_PMU_EVENT_TYPE_HW_CACHE << SBI_PMU_EVENT_IDX_TYPE_OFFSET | \
>+	SBI_PMU_HW_CACHE_##id << 3 | SBI_PMU_HW_CACHE_OP_##op << 1 | \
>+	SBI_PMU_HW_CACHE_RESULT_##res)
>+
> /**
>  * Special "firmware" events provided by the OpenSBI, even if the hardware
>  * does not support performance events. These events are encoded as a raw
>diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
>index d6dafef..fbcd870 100644
>--- a/platform/generic/Kconfig
>+++ b/platform/generic/Kconfig
>@@ -31,6 +31,7 @@ config PLATFORM_ALLWINNER_D1
> config PLATFORM_ANDES_AE350
> 	bool "Andes AE350 support"
> 	select SYS_ATCSMU
>+	select ANDES_HPM
> 	select ANDES_PMU
> 	default n
>
>@@ -38,6 +39,7 @@ config PLATFORM_RENESAS_RZFIVE
> 	bool "Renesas RZ/Five support"
> 	select ANDES45_PMA
> 	select ANDES_SBI
>+	select ANDES_HPM
> 	select ANDES_PMU
> 	default n
>
>diff --git a/platform/generic/andes/Kconfig b/platform/generic/andes/Kconfig
>index 555e4fe..8af2704 100644
>--- a/platform/generic/andes/Kconfig
>+++ b/platform/generic/andes/Kconfig
>@@ -8,6 +8,13 @@ config ANDES_SBI
> 	bool "Andes SBI support"
> 	default n
>
>+config ANDES_HPM
>+	bool "Andes HPM support"
>+	default n
>+	help
>+	  This provides Andes platform override for creating
>+	  event to counter mappings in pmu node.
>+
> config ANDES_PMU
> 	bool "Andes custom PMU extension support"
> 	default n
>diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
>index 80cd294..28c187e 100644
>--- a/platform/generic/andes/ae350.c
>+++ b/platform/generic/andes/ae350.c
>@@ -19,6 +19,7 @@
> #include <sbi/sbi_ipi.h>
> #include <sbi/sbi_init.h>
> #include <andes/andes45.h>
>+#include <andes/andes_hpm.h>
>
> static struct smu_data smu = { 0 };
> extern void __ae350_enable_coherency_warmboot(void);
>@@ -128,5 +129,6 @@ static const struct fdt_match andes_ae350_match[] = {
> const struct platform_override andes_ae350 = {
> 	.match_table = andes_ae350_match,
> 	.final_init  = ae350_final_init,
>+	.fdt_add_pmu_mappings = andes_fdt_add_pmu_mappings,
> 	.extensions_init = ae350_extensions_init,
> };
>diff --git a/platform/generic/andes/andes_hpm.c b/platform/generic/andes/andes_hpm.c
>new file mode 100644
>index 0000000..f9e6d2e
>--- /dev/null
>+++ b/platform/generic/andes/andes_hpm.c
>@@ -0,0 +1,381 @@
>+/*
>+ * SPDX-License-Identifier: BSD-2-Clause
>+ *
>+ * Copyright (c) 2023 Andes Technology Corporation
>+ */
>+
>+#include <andes/andes45.h>
>+#include <andes/andes_hpm.h>
>+#include <sbi/riscv_asm.h>
>+#include <sbi/sbi_ecall_interface.h>
>+#include <sbi_utils/fdt/fdt_fixup.h>
>+
>+static const struct sbi_pmu_event_select_map andes45_hw_evt_selects[] = {
>+	/* Hardware general events (Type #0) */
>+	{
>+		/* perf: cycles (eidx: 0x1) */
>+		.eidx = SBI_PMU_HW_CPU_CYCLES,
>+		.select = ANDES_CYCLES
>+	},
>+	{
>+		/* perf: instructions (eidx: 0x2) */
>+		.eidx = SBI_PMU_HW_INSTRUCTIONS,
>+		.select = ANDES_INSTRET
>+	},
>+	{
>+		/* perf: cache-references (eidx: 0x3) */
>+		.eidx = SBI_PMU_HW_CACHE_REFERENCES,
>+		.select = ANDES_DCACHE_ACCESS
>+	},
>+	{
>+		/* perf: cache-misses (eidx: 0x4) */
>+		.eidx = SBI_PMU_HW_CACHE_MISSES,
>+		.select = ANDES_DCACHE_MISS
>+	},
>+	{
>+		/* perf: branches (eidx: 0x5) */
>+		.eidx = SBI_PMU_HW_BRANCH_INSTRUCTIONS,
>+		.select = ANDES_CONDITION_BR,
>+	},
>+	{
>+		/* perf: branch-misses (eidx: 0x6) */
>+		.eidx = SBI_PMU_HW_BRANCH_MISSES,
>+		.select = ANDES_MISPREDICT_CONDITION_BR,
>+	},
>+	/* Hardware cache events (Type #1) */
>+	{
>+		/* perf: L1-dcache-loads (eidx: 0x10000) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, ACCESS),
>+		.select = ANDES_DCACHE_LOAD_ACCESS
>+	},
>+	{
>+		/* perf: L1-dcache-loads-misses (eidx: 0x10001) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, MISS),
>+		.select = ANDES_DCACHE_LOAD_MISS
>+	},
>+	{
>+		/* perf: L1-dcache-stores (eidx: 0x10002) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, ACCESS),
>+		.select = ANDES_DCACHE_STORE_ACCESS
>+	},
>+	{
>+		/* perf: L1-dcache-store-misses (eidx: 0x10003) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, MISS),
>+		.select = ANDES_DCACHE_STORE_MISS
>+	},
>+	{
>+		/* perf: L1-icache-load (eidx: 0x10008) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, ACCESS),
>+		.select = ANDES_ICACHE_ACCESS
>+	},
>+	{
>+		/* perf: L1-icache-load-misses (eidx: 0x10009) */
>+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, MISS),
>+		.select = ANDES_ICACHE_MISS
>+	},
>+	{ /* sentinel */ }
>+};
>+
>+static const struct sbi_pmu_event_counter_map andes45_hw_evt_counters[] = {
>+	{
>+		/* perf: cycles (eidx: 0x1) */
>+		.eidx_start = SBI_PMU_HW_CPU_CYCLES,
>+		/* perf: branch-misses (eidx: 0x6) */
>+		.eidx_end = SBI_PMU_HW_BRANCH_MISSES,
>+		.ctr_map = ANDES_MHPM_MAP,
>+	},
>+	{
>+		/* perf: L1-dcache-loads (eidx: 0x10000) */
>+		.eidx_start = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, ACCESS),
>+		/* perf: L1-dcache-store-misses (eidx: 0x10003) */
>+		.eidx_end = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, MISS),
>+		.ctr_map = ANDES_MHPM_MAP,
>+	},
>+	{
>+		/* perf: L1-icache-load (eidx: 0x10008) */
>+		.eidx_start = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, ACCESS),
>+		/* perf: L1-icache-load-misses (eidx: 0x10009) */
>+		.eidx_end = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, MISS),
>+		.ctr_map = ANDES_MHPM_MAP,
>+	},
>+	{ /* sentinel */ }
>+};
>+
>+static const struct sbi_pmu_raw_event_counter_map andes45_raw_evt_counters[] = {
>+	{
>+		.select = ANDES_CYCLES,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INSTRET,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_LOAD_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_STORE_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_ATOMIC_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_SYS_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_COMPUTE_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CONDITION_BR,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_TAKEN_CONDITION_BR,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_JAL_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_JALR_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_RET_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CONTROL_TRANS_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_EX9_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_MUL_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_DIV_REMAINDER_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_LOAD_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_STORE_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_ADD_SUB_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_MUL_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_FUSED_MULADD_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_FLOAT_DIV_SQUARE_ROOT_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_OTHER_FLOAT_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_INT_MUL_AND_SUB_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_RETIRED_OP,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_ILM_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DLM_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_ICACHE_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_ICACHE_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_LOAD_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_LOAD_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_STORE_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_STORE_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_DCACHE_WB,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CYCLE_WAIT_ICACHE_FILL,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CYCLE_WAIT_DCACHE_FILL,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_UNCACHED_IFETCH_FROM_BUS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_UNCACHED_LOAD_FROM_BUS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CYCLE_WAIT_UNCACHED_IFETCH,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CYCLE_WAIT_UNCACHED_LOAD,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MAIN_ITLB_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MAIN_ITLB_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MAIN_DTLB_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MAIN_DTLB_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_CYCLE_WAIT_ITLB_FILL,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_PIPE_STALL_CYCLE_DTLB_MISS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_HW_PREFETCH_BUS_ACCESS,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MISPREDICT_CONDITION_BR,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MISPREDICT_TAKE_CONDITION_BR,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{
>+		.select = ANDES_MISPREDICT_TARGET_RET_INST,
>+		.select_mask = ANDES_RAW_EVENT_MASK,
>+		.ctr_map = ANDES_MHPM_MAP
>+	},
>+	{ /* sentinel */ }
>+};

Why not use DT? I just see everything is fixed.

Adding fdt_add_pmu_mappings is not a good idea. Opensbi can use its own
DT by setting FW_FDT_PATH, so you do not need to add this mapping table.

>+
>+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match)
>+{
>+	/*
>+	 * At the moment, simply create mapping for any 45-series core
>+	 * based on marchid, we may check and differentiate the mapping
>+	 * by mimpid.
>+	 */
>+	if (is_andes(45))
>+		return fdt_add_pmu_mappings(fdt, andes45_hw_evt_selects,
>+					    andes45_hw_evt_counters,
>+					    andes45_raw_evt_counters);
>+
>+	return 0;
>+}
>diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk
>index 6a8c66c..57caaf6 100644
>--- a/platform/generic/andes/objects.mk
>+++ b/platform/generic/andes/objects.mk
>@@ -8,3 +8,4 @@ platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o
> platform-objs-$(CONFIG_ANDES45_PMA) += andes/andes45-pma.o
> platform-objs-$(CONFIG_ANDES_SBI) += andes/andes_sbi.o
> platform-objs-$(CONFIG_ANDES_PMU) += andes/andes_pmu.o
>+platform-objs-$(CONFIG_ANDES_HPM) += andes/andes_hpm.o
>diff --git a/platform/generic/include/andes/andes_hpm.h b/platform/generic/include/andes/andes_hpm.h
>new file mode 100644
>index 0000000..0f301db
>--- /dev/null
>+++ b/platform/generic/include/andes/andes_hpm.h
>@@ -0,0 +1,83 @@
>+/*
>+ * SPDX-License-Identifier: BSD-2-Clause
>+ *
>+ * Copyright (c) 2023 Andes Technology Corporation
>+ */
>+
>+#ifndef _ANDES_HPM_H_
>+#define _ANDES_HPM_H_
>+
>+#include <sbi_utils/fdt/fdt_helper.h>
>+
>+#define ANDES_MHPM_MAP		0x78
>+#define ANDES_RAW_EVENT_MASK	~0ULL
>+
>+/* Event code for instruction commit events */
>+#define ANDES_CYCLES				0x10
>+#define ANDES_INSTRET				0x20
>+#define ANDES_INT_LOAD_INST			0x30
>+#define ANDES_INT_STORE_INST			0x40
>+#define ANDES_ATOMIC_INST			0x50
>+#define ANDES_SYS_INST				0x60
>+#define ANDES_INT_COMPUTE_INST			0x70
>+#define ANDES_CONDITION_BR			0x80
>+#define ANDES_TAKEN_CONDITION_BR		0x90
>+#define ANDES_JAL_INST				0xA0
>+#define ANDES_JALR_INST				0xB0
>+#define ANDES_RET_INST				0xC0
>+#define ANDES_CONTROL_TRANS_INST		0xD0
>+#define ANDES_EX9_INST				0xE0
>+#define ANDES_INT_MUL_INST			0xF0
>+#define ANDES_INT_DIV_REMAINDER_INST		0x100
>+#define ANDES_FLOAT_LOAD_INST			0x110
>+#define ANDES_FLOAT_STORE_INST			0x120
>+#define ANDES_FLOAT_ADD_SUB_INST		0x130
>+#define ANDES_FLOAT_MUL_INST			0x140
>+#define ANDES_FLOAT_FUSED_MULADD_INST		0x150
>+#define ANDES_FLOAT_DIV_SQUARE_ROOT_INST	0x160
>+#define ANDES_OTHER_FLOAT_INST			0x170
>+#define ANDES_INT_MUL_AND_SUB_INST		0x180
>+#define ANDES_RETIRED_OP			0x190
>+
>+/* Event code for memory system events */
>+#define ANDES_ILM_ACCESS			0x01
>+#define ANDES_DLM_ACCESS			0x11
>+#define ANDES_ICACHE_ACCESS			0x21
>+#define ANDES_ICACHE_MISS			0x31
>+#define ANDES_DCACHE_ACCESS			0x41
>+#define ANDES_DCACHE_MISS			0x51
>+#define ANDES_DCACHE_LOAD_ACCESS		0x61
>+#define ANDES_DCACHE_LOAD_MISS			0x71
>+#define ANDES_DCACHE_STORE_ACCESS		0x81
>+#define ANDES_DCACHE_STORE_MISS			0x91
>+#define ANDES_DCACHE_WB				0xA1
>+#define ANDES_CYCLE_WAIT_ICACHE_FILL		0xB1
>+#define ANDES_CYCLE_WAIT_DCACHE_FILL		0xC1
>+#define ANDES_UNCACHED_IFETCH_FROM_BUS		0xD1
>+#define ANDES_UNCACHED_LOAD_FROM_BUS		0xE1
>+#define ANDES_CYCLE_WAIT_UNCACHED_IFETCH	0xF1
>+#define ANDES_CYCLE_WAIT_UNCACHED_LOAD		0x101
>+#define ANDES_MAIN_ITLB_ACCESS			0x111
>+#define ANDES_MAIN_ITLB_MISS			0x121
>+#define ANDES_MAIN_DTLB_ACCESS			0x131
>+#define ANDES_MAIN_DTLB_MISS			0x141
>+#define ANDES_CYCLE_WAIT_ITLB_FILL		0x151
>+#define ANDES_PIPE_STALL_CYCLE_DTLB_MISS	0x161
>+#define ANDES_HW_PREFETCH_BUS_ACCESS		0x171
>+
>+/* Event code for microarchitecture events */
>+#define ANDES_MISPREDICT_CONDITION_BR		0x02
>+#define ANDES_MISPREDICT_TAKE_CONDITION_BR	0x12
>+#define ANDES_MISPREDICT_TARGET_RET_INST	0x22
>+
>+#ifdef CONFIG_ANDES_HPM
>+
>+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match);
>+
>+#else
>+
>+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match) { return 0; }
>+
>+#endif /* CONFIG_ANDES_HPM */
>+
>+#endif /* _ANDES_HPM_H_ */
>diff --git a/platform/generic/renesas/rzfive/rzfive.c b/platform/generic/renesas/rzfive/rzfive.c
>index 2f772c8..fafbff0 100644
>--- a/platform/generic/renesas/rzfive/rzfive.c
>+++ b/platform/generic/renesas/rzfive/rzfive.c
>@@ -5,6 +5,7 @@
>  */
>
> #include <andes/andes45_pma.h>
>+#include <andes/andes_hpm.h>
> #include <andes/andes_pmu.h>
> #include <andes/andes_sbi.h>
> #include <platform_override.h>
>@@ -69,6 +70,7 @@ const struct platform_override renesas_rzfive = {
> 	.match_table = renesas_rzfive_match,
> 	.early_init = renesas_rzfive_early_init,
> 	.final_init = renesas_rzfive_final_init,
>+	.fdt_add_pmu_mappings = andes_fdt_add_pmu_mappings,
> 	.vendor_ext_provider = andes_sbi_vendor_ext_provider,
> 	.extensions_init = renesas_rzfive_extensions_init,
> };
>--
>2.34.1
>
>
Yu-Chien Peter Lin Oct. 22, 2023, 6:50 a.m. UTC | #2
Hi Inochi Amaoto,

On Sat, Oct 21, 2023 at 08:25:30PM +0800, Inochi Amaoto wrote:
> >Implement the andes_fdt_add_pmu_mappings() callback, which creates PMU
> >node properties that will later be populated with mapping tables.
> >
> >Currently we only support 45-series event mappings.
> >
> >Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
> >---
> >Changes v1 -> v2:
> >  - New patch
> >---
> > include/sbi/sbi_ecall_interface.h          |   5 +
> > platform/generic/Kconfig                   |   2 +
> > platform/generic/andes/Kconfig             |   7 +
> > platform/generic/andes/ae350.c             |   2 +
> > platform/generic/andes/andes_hpm.c         | 381 +++++++++++++++++++++
> > platform/generic/andes/objects.mk          |   1 +
> > platform/generic/include/andes/andes_hpm.h |  83 +++++
> > platform/generic/renesas/rzfive/rzfive.c   |   2 +
> > 8 files changed, 483 insertions(+)
> > create mode 100644 platform/generic/andes/andes_hpm.c
> > create mode 100644 platform/generic/include/andes/andes_hpm.h
> >
> >diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h
> >index 1fe469e..89187e7 100644
> >--- a/include/sbi/sbi_ecall_interface.h
> >+++ b/include/sbi/sbi_ecall_interface.h
> >@@ -155,6 +155,11 @@ enum sbi_pmu_hw_cache_op_result_id {
> > 	SBI_PMU_HW_CACHE_RESULT_MAX,
> > };
> >
> >+#define SBI_PMU_HW_CACHE_EVENT_IDX(id, op, res) \
> >+	(SBI_PMU_EVENT_TYPE_HW_CACHE << SBI_PMU_EVENT_IDX_TYPE_OFFSET | \
> >+	SBI_PMU_HW_CACHE_##id << 3 | SBI_PMU_HW_CACHE_OP_##op << 1 | \
> >+	SBI_PMU_HW_CACHE_RESULT_##res)
> >+
> > /**
> >  * Special "firmware" events provided by the OpenSBI, even if the hardware
> >  * does not support performance events. These events are encoded as a raw
> >diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> >index d6dafef..fbcd870 100644
> >--- a/platform/generic/Kconfig
> >+++ b/platform/generic/Kconfig
> >@@ -31,6 +31,7 @@ config PLATFORM_ALLWINNER_D1
> > config PLATFORM_ANDES_AE350
> > 	bool "Andes AE350 support"
> > 	select SYS_ATCSMU
> >+	select ANDES_HPM
> > 	select ANDES_PMU
> > 	default n
> >
> >@@ -38,6 +39,7 @@ config PLATFORM_RENESAS_RZFIVE
> > 	bool "Renesas RZ/Five support"
> > 	select ANDES45_PMA
> > 	select ANDES_SBI
> >+	select ANDES_HPM
> > 	select ANDES_PMU
> > 	default n
> >
> >diff --git a/platform/generic/andes/Kconfig b/platform/generic/andes/Kconfig
> >index 555e4fe..8af2704 100644
> >--- a/platform/generic/andes/Kconfig
> >+++ b/platform/generic/andes/Kconfig
> >@@ -8,6 +8,13 @@ config ANDES_SBI
> > 	bool "Andes SBI support"
> > 	default n
> >
> >+config ANDES_HPM
> >+	bool "Andes HPM support"
> >+	default n
> >+	help
> >+	  This provides Andes platform override for creating
> >+	  event to counter mappings in pmu node.
> >+
> > config ANDES_PMU
> > 	bool "Andes custom PMU extension support"
> > 	default n
> >diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
> >index 80cd294..28c187e 100644
> >--- a/platform/generic/andes/ae350.c
> >+++ b/platform/generic/andes/ae350.c
> >@@ -19,6 +19,7 @@
> > #include <sbi/sbi_ipi.h>
> > #include <sbi/sbi_init.h>
> > #include <andes/andes45.h>
> >+#include <andes/andes_hpm.h>
> >
> > static struct smu_data smu = { 0 };
> > extern void __ae350_enable_coherency_warmboot(void);
> >@@ -128,5 +129,6 @@ static const struct fdt_match andes_ae350_match[] = {
> > const struct platform_override andes_ae350 = {
> > 	.match_table = andes_ae350_match,
> > 	.final_init  = ae350_final_init,
> >+	.fdt_add_pmu_mappings = andes_fdt_add_pmu_mappings,
> > 	.extensions_init = ae350_extensions_init,
> > };
> >diff --git a/platform/generic/andes/andes_hpm.c b/platform/generic/andes/andes_hpm.c
> >new file mode 100644
> >index 0000000..f9e6d2e
> >--- /dev/null
> >+++ b/platform/generic/andes/andes_hpm.c
> >@@ -0,0 +1,381 @@
> >+/*
> >+ * SPDX-License-Identifier: BSD-2-Clause
> >+ *
> >+ * Copyright (c) 2023 Andes Technology Corporation
> >+ */
> >+
> >+#include <andes/andes45.h>
> >+#include <andes/andes_hpm.h>
> >+#include <sbi/riscv_asm.h>
> >+#include <sbi/sbi_ecall_interface.h>
> >+#include <sbi_utils/fdt/fdt_fixup.h>
> >+
> >+static const struct sbi_pmu_event_select_map andes45_hw_evt_selects[] = {
> >+	/* Hardware general events (Type #0) */
> >+	{
> >+		/* perf: cycles (eidx: 0x1) */
> >+		.eidx = SBI_PMU_HW_CPU_CYCLES,
> >+		.select = ANDES_CYCLES
> >+	},
> >+	{
> >+		/* perf: instructions (eidx: 0x2) */
> >+		.eidx = SBI_PMU_HW_INSTRUCTIONS,
> >+		.select = ANDES_INSTRET
> >+	},
> >+	{
> >+		/* perf: cache-references (eidx: 0x3) */
> >+		.eidx = SBI_PMU_HW_CACHE_REFERENCES,
> >+		.select = ANDES_DCACHE_ACCESS
> >+	},
> >+	{
> >+		/* perf: cache-misses (eidx: 0x4) */
> >+		.eidx = SBI_PMU_HW_CACHE_MISSES,
> >+		.select = ANDES_DCACHE_MISS
> >+	},
> >+	{
> >+		/* perf: branches (eidx: 0x5) */
> >+		.eidx = SBI_PMU_HW_BRANCH_INSTRUCTIONS,
> >+		.select = ANDES_CONDITION_BR,
> >+	},
> >+	{
> >+		/* perf: branch-misses (eidx: 0x6) */
> >+		.eidx = SBI_PMU_HW_BRANCH_MISSES,
> >+		.select = ANDES_MISPREDICT_CONDITION_BR,
> >+	},
> >+	/* Hardware cache events (Type #1) */
> >+	{
> >+		/* perf: L1-dcache-loads (eidx: 0x10000) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, ACCESS),
> >+		.select = ANDES_DCACHE_LOAD_ACCESS
> >+	},
> >+	{
> >+		/* perf: L1-dcache-loads-misses (eidx: 0x10001) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, MISS),
> >+		.select = ANDES_DCACHE_LOAD_MISS
> >+	},
> >+	{
> >+		/* perf: L1-dcache-stores (eidx: 0x10002) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, ACCESS),
> >+		.select = ANDES_DCACHE_STORE_ACCESS
> >+	},
> >+	{
> >+		/* perf: L1-dcache-store-misses (eidx: 0x10003) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, MISS),
> >+		.select = ANDES_DCACHE_STORE_MISS
> >+	},
> >+	{
> >+		/* perf: L1-icache-load (eidx: 0x10008) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, ACCESS),
> >+		.select = ANDES_ICACHE_ACCESS
> >+	},
> >+	{
> >+		/* perf: L1-icache-load-misses (eidx: 0x10009) */
> >+		.eidx = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, MISS),
> >+		.select = ANDES_ICACHE_MISS
> >+	},
> >+	{ /* sentinel */ }
> >+};
> >+
> >+static const struct sbi_pmu_event_counter_map andes45_hw_evt_counters[] = {
> >+	{
> >+		/* perf: cycles (eidx: 0x1) */
> >+		.eidx_start = SBI_PMU_HW_CPU_CYCLES,
> >+		/* perf: branch-misses (eidx: 0x6) */
> >+		.eidx_end = SBI_PMU_HW_BRANCH_MISSES,
> >+		.ctr_map = ANDES_MHPM_MAP,
> >+	},
> >+	{
> >+		/* perf: L1-dcache-loads (eidx: 0x10000) */
> >+		.eidx_start = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, READ, ACCESS),
> >+		/* perf: L1-dcache-store-misses (eidx: 0x10003) */
> >+		.eidx_end = SBI_PMU_HW_CACHE_EVENT_IDX(L1D, WRITE, MISS),
> >+		.ctr_map = ANDES_MHPM_MAP,
> >+	},
> >+	{
> >+		/* perf: L1-icache-load (eidx: 0x10008) */
> >+		.eidx_start = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, ACCESS),
> >+		/* perf: L1-icache-load-misses (eidx: 0x10009) */
> >+		.eidx_end = SBI_PMU_HW_CACHE_EVENT_IDX(L1I, READ, MISS),
> >+		.ctr_map = ANDES_MHPM_MAP,
> >+	},
> >+	{ /* sentinel */ }
> >+};
> >+
> >+static const struct sbi_pmu_raw_event_counter_map andes45_raw_evt_counters[] = {
> >+	{
> >+		.select = ANDES_CYCLES,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INSTRET,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_LOAD_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_STORE_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_ATOMIC_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_SYS_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_COMPUTE_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CONDITION_BR,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_TAKEN_CONDITION_BR,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_JAL_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_JALR_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_RET_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CONTROL_TRANS_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_EX9_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_MUL_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_DIV_REMAINDER_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_LOAD_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_STORE_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_ADD_SUB_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_MUL_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_FUSED_MULADD_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_FLOAT_DIV_SQUARE_ROOT_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_OTHER_FLOAT_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_INT_MUL_AND_SUB_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_RETIRED_OP,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_ILM_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DLM_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_ICACHE_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_ICACHE_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_LOAD_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_LOAD_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_STORE_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_STORE_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_DCACHE_WB,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CYCLE_WAIT_ICACHE_FILL,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CYCLE_WAIT_DCACHE_FILL,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_UNCACHED_IFETCH_FROM_BUS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_UNCACHED_LOAD_FROM_BUS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CYCLE_WAIT_UNCACHED_IFETCH,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CYCLE_WAIT_UNCACHED_LOAD,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MAIN_ITLB_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MAIN_ITLB_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MAIN_DTLB_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MAIN_DTLB_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_CYCLE_WAIT_ITLB_FILL,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_PIPE_STALL_CYCLE_DTLB_MISS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_HW_PREFETCH_BUS_ACCESS,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MISPREDICT_CONDITION_BR,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MISPREDICT_TAKE_CONDITION_BR,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{
> >+		.select = ANDES_MISPREDICT_TARGET_RET_INST,
> >+		.select_mask = ANDES_RAW_EVENT_MASK,
> >+		.ctr_map = ANDES_MHPM_MAP
> >+	},
> >+	{ /* sentinel */ }
> >+};
> 
> Why not use DT? I just see everything is fixed.
> 
> Adding fdt_add_pmu_mappings is not a good idea. Opensbi can use its own
> DT by setting FW_FDT_PATH, so you do not need to add this mapping table.

Thanks for the feedback.
We use this to ensure that a valid pmu node is available, but
if most people don't need this, I'm happy to drop this patch.

Best regards,
Peter Lin

> >+
> >+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match)
> >+{
> >+	/*
> >+	 * At the moment, simply create mapping for any 45-series core
> >+	 * based on marchid, we may check and differentiate the mapping
> >+	 * by mimpid.
> >+	 */
> >+	if (is_andes(45))
> >+		return fdt_add_pmu_mappings(fdt, andes45_hw_evt_selects,
> >+					    andes45_hw_evt_counters,
> >+					    andes45_raw_evt_counters);
> >+
> >+	return 0;
> >+}
> >diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk
> >index 6a8c66c..57caaf6 100644
> >--- a/platform/generic/andes/objects.mk
> >+++ b/platform/generic/andes/objects.mk
> >@@ -8,3 +8,4 @@ platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o
> > platform-objs-$(CONFIG_ANDES45_PMA) += andes/andes45-pma.o
> > platform-objs-$(CONFIG_ANDES_SBI) += andes/andes_sbi.o
> > platform-objs-$(CONFIG_ANDES_PMU) += andes/andes_pmu.o
> >+platform-objs-$(CONFIG_ANDES_HPM) += andes/andes_hpm.o
> >diff --git a/platform/generic/include/andes/andes_hpm.h b/platform/generic/include/andes/andes_hpm.h
> >new file mode 100644
> >index 0000000..0f301db
> >--- /dev/null
> >+++ b/platform/generic/include/andes/andes_hpm.h
> >@@ -0,0 +1,83 @@
> >+/*
> >+ * SPDX-License-Identifier: BSD-2-Clause
> >+ *
> >+ * Copyright (c) 2023 Andes Technology Corporation
> >+ */
> >+
> >+#ifndef _ANDES_HPM_H_
> >+#define _ANDES_HPM_H_
> >+
> >+#include <sbi_utils/fdt/fdt_helper.h>
> >+
> >+#define ANDES_MHPM_MAP		0x78
> >+#define ANDES_RAW_EVENT_MASK	~0ULL
> >+
> >+/* Event code for instruction commit events */
> >+#define ANDES_CYCLES				0x10
> >+#define ANDES_INSTRET				0x20
> >+#define ANDES_INT_LOAD_INST			0x30
> >+#define ANDES_INT_STORE_INST			0x40
> >+#define ANDES_ATOMIC_INST			0x50
> >+#define ANDES_SYS_INST				0x60
> >+#define ANDES_INT_COMPUTE_INST			0x70
> >+#define ANDES_CONDITION_BR			0x80
> >+#define ANDES_TAKEN_CONDITION_BR		0x90
> >+#define ANDES_JAL_INST				0xA0
> >+#define ANDES_JALR_INST				0xB0
> >+#define ANDES_RET_INST				0xC0
> >+#define ANDES_CONTROL_TRANS_INST		0xD0
> >+#define ANDES_EX9_INST				0xE0
> >+#define ANDES_INT_MUL_INST			0xF0
> >+#define ANDES_INT_DIV_REMAINDER_INST		0x100
> >+#define ANDES_FLOAT_LOAD_INST			0x110
> >+#define ANDES_FLOAT_STORE_INST			0x120
> >+#define ANDES_FLOAT_ADD_SUB_INST		0x130
> >+#define ANDES_FLOAT_MUL_INST			0x140
> >+#define ANDES_FLOAT_FUSED_MULADD_INST		0x150
> >+#define ANDES_FLOAT_DIV_SQUARE_ROOT_INST	0x160
> >+#define ANDES_OTHER_FLOAT_INST			0x170
> >+#define ANDES_INT_MUL_AND_SUB_INST		0x180
> >+#define ANDES_RETIRED_OP			0x190
> >+
> >+/* Event code for memory system events */
> >+#define ANDES_ILM_ACCESS			0x01
> >+#define ANDES_DLM_ACCESS			0x11
> >+#define ANDES_ICACHE_ACCESS			0x21
> >+#define ANDES_ICACHE_MISS			0x31
> >+#define ANDES_DCACHE_ACCESS			0x41
> >+#define ANDES_DCACHE_MISS			0x51
> >+#define ANDES_DCACHE_LOAD_ACCESS		0x61
> >+#define ANDES_DCACHE_LOAD_MISS			0x71
> >+#define ANDES_DCACHE_STORE_ACCESS		0x81
> >+#define ANDES_DCACHE_STORE_MISS			0x91
> >+#define ANDES_DCACHE_WB				0xA1
> >+#define ANDES_CYCLE_WAIT_ICACHE_FILL		0xB1
> >+#define ANDES_CYCLE_WAIT_DCACHE_FILL		0xC1
> >+#define ANDES_UNCACHED_IFETCH_FROM_BUS		0xD1
> >+#define ANDES_UNCACHED_LOAD_FROM_BUS		0xE1
> >+#define ANDES_CYCLE_WAIT_UNCACHED_IFETCH	0xF1
> >+#define ANDES_CYCLE_WAIT_UNCACHED_LOAD		0x101
> >+#define ANDES_MAIN_ITLB_ACCESS			0x111
> >+#define ANDES_MAIN_ITLB_MISS			0x121
> >+#define ANDES_MAIN_DTLB_ACCESS			0x131
> >+#define ANDES_MAIN_DTLB_MISS			0x141
> >+#define ANDES_CYCLE_WAIT_ITLB_FILL		0x151
> >+#define ANDES_PIPE_STALL_CYCLE_DTLB_MISS	0x161
> >+#define ANDES_HW_PREFETCH_BUS_ACCESS		0x171
> >+
> >+/* Event code for microarchitecture events */
> >+#define ANDES_MISPREDICT_CONDITION_BR		0x02
> >+#define ANDES_MISPREDICT_TAKE_CONDITION_BR	0x12
> >+#define ANDES_MISPREDICT_TARGET_RET_INST	0x22
> >+
> >+#ifdef CONFIG_ANDES_HPM
> >+
> >+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match);
> >+
> >+#else
> >+
> >+int andes_fdt_add_pmu_mappings(void *fdt, const struct fdt_match *match) { return 0; }
> >+
> >+#endif /* CONFIG_ANDES_HPM */
> >+
> >+#endif /* _ANDES_HPM_H_ */
> >diff --git a/platform/generic/renesas/rzfive/rzfive.c b/platform/generic/renesas/rzfive/rzfive.c
> >index 2f772c8..fafbff0 100644
> >--- a/platform/generic/renesas/rzfive/rzfive.c
> >+++ b/platform/generic/renesas/rzfive/rzfive.c
> >@@ -5,6 +5,7 @@
> >  */
> >
> > #include <andes/andes45_pma.h>
> >+#include <andes/andes_hpm.h>
> > #include <andes/andes_pmu.h>
> > #include <andes/andes_sbi.h>
> > #include <platform_override.h>
> >@@ -69,6 +70,7 @@ const struct platform_override renesas_rzfive = {
> > 	.match_table = renesas_rzfive_match,
> > 	.early_init = renesas_rzfive_early_init,
> > 	.final_init = renesas_rzfive_final_init,
> >+	.fdt_add_pmu_mappings = andes_fdt_add_pmu_mappings,
> > 	.vendor_ext_provider = andes_sbi_vendor_ext_provider,
> > 	.extensions_init = renesas_rzfive_extensions_init,
> > };
> >--
> >2.34.1
> >
> >
Anup Patel Nov. 16, 2023, 6:59 a.m. UTC | #3
On Thu, Oct 19, 2023 at 5:11 PM Yu Chien Peter Lin
<peterlin@andestech.com> wrote:
>
> Add fdt_add_pmu_mappings() that creates entries of riscv,*event-to-mhpm*
> property from arrays right before fdt_pmu_setup() populating the mapping
> tables (i.e. hw_event_map[] and fdt_pmu_evt_select[]).
>
> The helper function will skip the creation of those properties if a
> "/pmu" node with "riscv,pmu" compatible string is provided.
>
> Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>

This patch can be dropped. See my comment in PATCH10.

Regards,
Anup

> ---
> Changes v1 -> v2:
>   - New patch
> ---
>  include/sbi_utils/fdt/fdt_fixup.h            | 48 ++++++++++
>  lib/utils/fdt/fdt_fixup.c                    | 95 ++++++++++++++++++++
>  platform/generic/include/platform_override.h |  1 +
>  platform/generic/platform.c                  | 10 ++-
>  4 files changed, 153 insertions(+), 1 deletion(-)
>
> diff --git a/include/sbi_utils/fdt/fdt_fixup.h b/include/sbi_utils/fdt/fdt_fixup.h
> index ecd55a7..9b72df8 100644
> --- a/include/sbi_utils/fdt/fdt_fixup.h
> +++ b/include/sbi_utils/fdt/fdt_fixup.h
> @@ -19,6 +19,54 @@ struct sbi_cpu_idle_state {
>         uint32_t wakeup_latency_us;
>  };
>
> +struct sbi_pmu_event_select_map {
> +       /**
> +        * The description of an entry in
> +        * riscv,event-to-mhpmevent property
> +        */
> +       uint32_t eidx;
> +       uint64_t select;
> +};
> +
> +struct sbi_pmu_event_counter_map {
> +       /**
> +        * The description of an entry in
> +        * riscv,event-to-mhpmcounters property
> +        */
> +       uint32_t eidx_start;
> +       uint32_t eidx_end;
> +       uint32_t ctr_map;
> +};
> +
> +struct sbi_pmu_raw_event_counter_map {
> +       /**
> +        * The description of an entry in
> +        * riscv,raw-event-to-mhpmcounters property
> +        */
> +       uint64_t select;
> +       uint64_t select_mask;
> +       uint32_t ctr_map;
> +};
> +
> +/**
> + * Add PMU properties in the DT
> + *
> + * Add information about event to selector/counter mappings to the
> + * devicetree.
> + *
> + * @param fdt: device tree blob
> + * @param selects: array of event index to selector value mapping
> + *                 descriptions, ending with empty element
> + * @param counters: array of event indexes to counters mapping
> + *                  descriptions, ending with empty element
> + * @param rcounters: array of raw events to counters mapping
> + *                   descriptions, ending with empty element
> + * @return zero on success and -ve on failure
> + */
> +int fdt_add_pmu_mappings(void *fdt, const struct sbi_pmu_event_select_map *selects,
> +               const struct sbi_pmu_event_counter_map *counters,
> +               const struct sbi_pmu_raw_event_counter_map *rcounters);
> +
>  /**
>   * Add CPU idle states to cpu nodes in the DT
>   *
> diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c
> index e213ded..67e2e2e 100644
> --- a/lib/utils/fdt/fdt_fixup.c
> +++ b/lib/utils/fdt/fdt_fixup.c
> @@ -20,6 +20,101 @@
>  #include <sbi_utils/fdt/fdt_pmu.h>
>  #include <sbi_utils/fdt/fdt_helper.h>
>
> +int fdt_add_pmu_mappings(void *fdt, const struct sbi_pmu_event_select_map *selects,
> +               const struct sbi_pmu_event_counter_map *counters,
> +               const struct sbi_pmu_raw_event_counter_map *rcounters)
> +{
> +       int i, err, pmu_noff, root_noff;
> +       const char *comp;
> +       fdt32_t evt_to_mhpmevent[3];
> +       fdt32_t evt_to_mhpmcounters[3];
> +       fdt32_t raw_evt_to_mhpmcounters[5];
> +
> +       /* Try to locate pmu node */
> +       pmu_noff = fdt_path_offset(fdt, "/pmu");
> +
> +       if (pmu_noff > 0) {
> +               /*
> +                * If compatible string is "riscv,pmu",
> +                * we assume a valid pmu node has been
> +                * provided.
> +                */
> +               comp = fdt_getprop(fdt, pmu_noff, "compatible", NULL);
> +               if (comp && !strcmp(comp, "riscv,pmu"))
> +                       return 0;
> +               else
> +                       return -FDT_ERR_BADVALUE;
> +       }
> +
> +       if (pmu_noff < 0 && pmu_noff != -FDT_ERR_NOTFOUND)
> +               return pmu_noff;
> +
> +       /*
> +        * If "riscv,event-to-mhpmevent" is present, "riscv,event-to-mhpmcounters"
> +        * must be provided as well, but not vice versa (OpenSBI will direct mapping
> +        * event_idx as selector value).
> +        */
> +       if (selects && !counters) {
> +               sbi_printf("%s: ERR: riscv,event-to-mhpmcounters is mandatory if"
> +                       " riscv,event-to-mhpmevent is present.", __func__);
> +               return SBI_EINVAL;
> +       }
> +
> +       /*
> +        * Create pmu node based on given @selects, @counters
> +        * and @rcounters.
> +        */
> +       root_noff = fdt_path_offset(fdt, "/");
> +       pmu_noff = fdt_add_subnode(fdt, root_noff, "pmu");
> +       if  (pmu_noff < 0)
> +               return pmu_noff;
> +
> +       err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
> +       if (err < 0)
> +               return err;
> +
> +       err = fdt_setprop_string(fdt, pmu_noff, "compatible", "riscv,pmu");
> +       if (err)
> +               return err;
> +
> +       /* Add riscv,event-to-mhpmevent */
> +       for (i = 0; selects && selects[i].eidx; i++) {
> +               evt_to_mhpmevent[0] = cpu_to_fdt32(selects[i].eidx);
> +               evt_to_mhpmevent[1] = cpu_to_fdt32(selects[i].select >> 32);
> +               evt_to_mhpmevent[2] = cpu_to_fdt32(selects[i].select & ~0UL);
> +               err = fdt_appendprop(fdt, pmu_noff, "riscv,event-to-mhpmevent",
> +                               evt_to_mhpmevent, 3 * sizeof(fdt32_t));
> +               if (err)
> +                       return err;
> +       }
> +
> +       /* Add riscv,event-to-mhpmcounters */
> +       for (i = 0; counters && counters[i].eidx_start; i++) {
> +               evt_to_mhpmcounters[0] = cpu_to_fdt32(counters[i].eidx_start);
> +               evt_to_mhpmcounters[1] = cpu_to_fdt32(counters[i].eidx_end);
> +               evt_to_mhpmcounters[2] = cpu_to_fdt32(counters[i].ctr_map);
> +               err = fdt_appendprop(fdt, pmu_noff, "riscv,event-to-mhpmcounters",
> +                               evt_to_mhpmcounters, 3 * sizeof(fdt32_t));
> +               if (err)
> +                       return err;
> +       }
> +
> +       /* Add riscv,raw-event-to-mhpmcounters */
> +       for (i = 0; rcounters && rcounters[i].select; i++) {
> +               raw_evt_to_mhpmcounters[0] = cpu_to_fdt32(rcounters[i].select >> 32);
> +               raw_evt_to_mhpmcounters[1] = cpu_to_fdt32(rcounters[i].select & ~0UL);
> +               raw_evt_to_mhpmcounters[2] = cpu_to_fdt32(rcounters[i].select_mask >> 32);
> +               raw_evt_to_mhpmcounters[3] = cpu_to_fdt32(rcounters[i].select_mask & ~0UL);
> +               raw_evt_to_mhpmcounters[4] = cpu_to_fdt32(rcounters[i].ctr_map);
> +               err = fdt_appendprop(fdt, pmu_noff, "riscv,raw-event-to-mhpmcounters",
> +                               raw_evt_to_mhpmcounters, 5 * sizeof(fdt32_t));
> +               if (err)
> +                       return err;
> +       }
> +
> +       return 0;
> +}
> +
>  int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
>  {
>         int cpu_node, cpus_node, err, idle_states_node;
> diff --git a/platform/generic/include/platform_override.h b/platform/generic/include/platform_override.h
> index bf4b112..bd34d2a 100644
> --- a/platform/generic/include/platform_override.h
> +++ b/platform/generic/include/platform_override.h
> @@ -19,6 +19,7 @@ struct platform_override {
>         u64 (*features)(const struct fdt_match *match);
>         u64 (*tlbr_flush_limit)(const struct fdt_match *match);
>         u32 (*tlb_num_entries)(const struct fdt_match *match);
> +       int (*fdt_add_pmu_mappings)(void *fdt, const struct fdt_match *match);
>         bool (*cold_boot_allowed)(u32 hartid, const struct fdt_match *match);
>         int (*early_init)(bool cold_boot, const struct fdt_match *match);
>         int (*final_init)(bool cold_boot, const struct fdt_match *match);
> diff --git a/platform/generic/platform.c b/platform/generic/platform.c
> index cb9270d..a48a8b7 100644
> --- a/platform/generic/platform.c
> +++ b/platform/generic/platform.c
> @@ -265,9 +265,17 @@ static u32 generic_tlb_num_entries(void)
>
>  static int generic_pmu_init(void)
>  {
> +       void *fdt = fdt_get_address();
>         int rc;
>
> -       rc = fdt_pmu_setup(fdt_get_address());
> +       if (generic_plat && generic_plat->fdt_add_pmu_mappings) {
> +               rc = generic_plat->fdt_add_pmu_mappings(fdt,
> +                                                       generic_plat_match);
> +               if (rc)
> +                       return rc;
> +       }
> +
> +       rc = fdt_pmu_setup(fdt);
>         if (rc && rc != SBI_ENOENT)
>                 return rc;
>
> --
> 2.34.1
>
diff mbox series

Patch

diff --git a/include/sbi_utils/fdt/fdt_fixup.h b/include/sbi_utils/fdt/fdt_fixup.h
index ecd55a7..9b72df8 100644
--- a/include/sbi_utils/fdt/fdt_fixup.h
+++ b/include/sbi_utils/fdt/fdt_fixup.h
@@ -19,6 +19,54 @@  struct sbi_cpu_idle_state {
 	uint32_t wakeup_latency_us;
 };
 
+struct sbi_pmu_event_select_map {
+	/**
+	 * The description of an entry in
+	 * riscv,event-to-mhpmevent property
+	 */
+	uint32_t eidx;
+	uint64_t select;
+};
+
+struct sbi_pmu_event_counter_map {
+	/**
+	 * The description of an entry in
+	 * riscv,event-to-mhpmcounters property
+	 */
+	uint32_t eidx_start;
+	uint32_t eidx_end;
+	uint32_t ctr_map;
+};
+
+struct sbi_pmu_raw_event_counter_map {
+	/**
+	 * The description of an entry in
+	 * riscv,raw-event-to-mhpmcounters property
+	 */
+	uint64_t select;
+	uint64_t select_mask;
+	uint32_t ctr_map;
+};
+
+/**
+ * Add PMU properties in the DT
+ *
+ * Add information about event to selector/counter mappings to the
+ * devicetree.
+ *
+ * @param fdt: device tree blob
+ * @param selects: array of event index to selector value mapping
+ *                 descriptions, ending with empty element
+ * @param counters: array of event indexes to counters mapping
+ *                  descriptions, ending with empty element
+ * @param rcounters: array of raw events to counters mapping
+ *                   descriptions, ending with empty element
+ * @return zero on success and -ve on failure
+ */
+int fdt_add_pmu_mappings(void *fdt, const struct sbi_pmu_event_select_map *selects,
+		const struct sbi_pmu_event_counter_map *counters,
+		const struct sbi_pmu_raw_event_counter_map *rcounters);
+
 /**
  * Add CPU idle states to cpu nodes in the DT
  *
diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c
index e213ded..67e2e2e 100644
--- a/lib/utils/fdt/fdt_fixup.c
+++ b/lib/utils/fdt/fdt_fixup.c
@@ -20,6 +20,101 @@ 
 #include <sbi_utils/fdt/fdt_pmu.h>
 #include <sbi_utils/fdt/fdt_helper.h>
 
+int fdt_add_pmu_mappings(void *fdt, const struct sbi_pmu_event_select_map *selects,
+		const struct sbi_pmu_event_counter_map *counters,
+		const struct sbi_pmu_raw_event_counter_map *rcounters)
+{
+	int i, err, pmu_noff, root_noff;
+	const char *comp;
+	fdt32_t evt_to_mhpmevent[3];
+	fdt32_t evt_to_mhpmcounters[3];
+	fdt32_t raw_evt_to_mhpmcounters[5];
+
+	/* Try to locate pmu node */
+	pmu_noff = fdt_path_offset(fdt, "/pmu");
+
+	if (pmu_noff > 0) {
+		/*
+		 * If compatible string is "riscv,pmu",
+		 * we assume a valid pmu node has been
+		 * provided.
+		 */
+		comp = fdt_getprop(fdt, pmu_noff, "compatible", NULL);
+		if (comp && !strcmp(comp, "riscv,pmu"))
+			return 0;
+		else
+			return -FDT_ERR_BADVALUE;
+	}
+
+	if (pmu_noff < 0 && pmu_noff != -FDT_ERR_NOTFOUND)
+		return pmu_noff;
+
+	/*
+	 * If "riscv,event-to-mhpmevent" is present, "riscv,event-to-mhpmcounters"
+	 * must be provided as well, but not vice versa (OpenSBI will direct mapping
+	 * event_idx as selector value).
+	 */
+	if (selects && !counters) {
+		sbi_printf("%s: ERR: riscv,event-to-mhpmcounters is mandatory if"
+			" riscv,event-to-mhpmevent is present.", __func__);
+		return SBI_EINVAL;
+	}
+
+	/*
+	 * Create pmu node based on given @selects, @counters
+	 * and @rcounters.
+	 */
+	root_noff = fdt_path_offset(fdt, "/");
+	pmu_noff = fdt_add_subnode(fdt, root_noff, "pmu");
+	if  (pmu_noff < 0)
+		return pmu_noff;
+
+	err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
+	if (err < 0)
+		return err;
+
+	err = fdt_setprop_string(fdt, pmu_noff, "compatible", "riscv,pmu");
+	if (err)
+		return err;
+
+	/* Add riscv,event-to-mhpmevent */
+	for (i = 0; selects && selects[i].eidx; i++) {
+		evt_to_mhpmevent[0] = cpu_to_fdt32(selects[i].eidx);
+		evt_to_mhpmevent[1] = cpu_to_fdt32(selects[i].select >> 32);
+		evt_to_mhpmevent[2] = cpu_to_fdt32(selects[i].select & ~0UL);
+		err = fdt_appendprop(fdt, pmu_noff, "riscv,event-to-mhpmevent",
+				evt_to_mhpmevent, 3 * sizeof(fdt32_t));
+		if (err)
+			return err;
+	}
+
+	/* Add riscv,event-to-mhpmcounters */
+	for (i = 0; counters && counters[i].eidx_start; i++) {
+		evt_to_mhpmcounters[0] = cpu_to_fdt32(counters[i].eidx_start);
+		evt_to_mhpmcounters[1] = cpu_to_fdt32(counters[i].eidx_end);
+		evt_to_mhpmcounters[2] = cpu_to_fdt32(counters[i].ctr_map);
+		err = fdt_appendprop(fdt, pmu_noff, "riscv,event-to-mhpmcounters",
+				evt_to_mhpmcounters, 3 * sizeof(fdt32_t));
+		if (err)
+			return err;
+	}
+
+	/* Add riscv,raw-event-to-mhpmcounters */
+	for (i = 0; rcounters && rcounters[i].select; i++) {
+		raw_evt_to_mhpmcounters[0] = cpu_to_fdt32(rcounters[i].select >> 32);
+		raw_evt_to_mhpmcounters[1] = cpu_to_fdt32(rcounters[i].select & ~0UL);
+		raw_evt_to_mhpmcounters[2] = cpu_to_fdt32(rcounters[i].select_mask >> 32);
+		raw_evt_to_mhpmcounters[3] = cpu_to_fdt32(rcounters[i].select_mask & ~0UL);
+		raw_evt_to_mhpmcounters[4] = cpu_to_fdt32(rcounters[i].ctr_map);
+		err = fdt_appendprop(fdt, pmu_noff, "riscv,raw-event-to-mhpmcounters",
+				raw_evt_to_mhpmcounters, 5 * sizeof(fdt32_t));
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
 {
 	int cpu_node, cpus_node, err, idle_states_node;
diff --git a/platform/generic/include/platform_override.h b/platform/generic/include/platform_override.h
index bf4b112..bd34d2a 100644
--- a/platform/generic/include/platform_override.h
+++ b/platform/generic/include/platform_override.h
@@ -19,6 +19,7 @@  struct platform_override {
 	u64 (*features)(const struct fdt_match *match);
 	u64 (*tlbr_flush_limit)(const struct fdt_match *match);
 	u32 (*tlb_num_entries)(const struct fdt_match *match);
+	int (*fdt_add_pmu_mappings)(void *fdt, const struct fdt_match *match);
 	bool (*cold_boot_allowed)(u32 hartid, const struct fdt_match *match);
 	int (*early_init)(bool cold_boot, const struct fdt_match *match);
 	int (*final_init)(bool cold_boot, const struct fdt_match *match);
diff --git a/platform/generic/platform.c b/platform/generic/platform.c
index cb9270d..a48a8b7 100644
--- a/platform/generic/platform.c
+++ b/platform/generic/platform.c
@@ -265,9 +265,17 @@  static u32 generic_tlb_num_entries(void)
 
 static int generic_pmu_init(void)
 {
+	void *fdt = fdt_get_address();
 	int rc;
 
-	rc = fdt_pmu_setup(fdt_get_address());
+	if (generic_plat && generic_plat->fdt_add_pmu_mappings) {
+		rc = generic_plat->fdt_add_pmu_mappings(fdt,
+							generic_plat_match);
+		if (rc)
+			return rc;
+	}
+
+	rc = fdt_pmu_setup(fdt);
 	if (rc && rc != SBI_ENOENT)
 		return rc;