public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
@ 2026-04-14  3:41 Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
                   ` (13 more replies)
  0 siblings, 14 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
trace drivers within the CoreSight framework and integrating them with perf tools.
The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
components, which can be directly leveraged through the existing CoreSight infrastructure.

Linux RISC-V trace support form Anup Patel:
(https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
which currently lacks ATB component support and guidance on reusing CoreSight components.

The series includes:
- RISC-V trace driver implementation within the CoreSight framework
- RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
- RISC-V trace PMU record capabilities and parsing events in perf.
- RISC-V Nexus Trace decoder for perf tools

Any comments or suggestions are welcome.

Verification on K3 SoC:
To verify this patch series on K3 hardware, the following device tree are required:
1. RISC-V Trace Encoder node (8)
2. RISC-V ATB Bridge node (8)
3. RISC-V Trace Funnel node (2)
3. CoreSight Funnel configuration for RISC-V (1)
4. CoreSight TMC configuration for trace buffer (1)

/{
        dummy_clk: apb-pclk {
                compatible = "fixed-clock";
                #clock-cells = <0x0>;
                clock-output-names = "clk14mhz";
                clock-frequency = <14000000>;
        };


        soc: soc {
                #address-cells = <2>;
                #size-cells = <2>;

                encoder0: encoder@d9002000 {
                        compatible = "riscv,trace-encoder";
                        reg = <0x0 0xd9002000 0x0 0x1000>;
                        cpus = <&cpu_0>;
                        out-ports {
                                port {
                                        cluster0_encoder0_out_port: endpoint {
                                                remote-endpoint = <&cluster0_bridge0_in_port>;
                                        };
                                };
                        };
                };

                bridge0: bridge@d9003000 {
                        compatible = "riscv,trace-atbbridge";
                        reg = <0x0 0xd9003000 0x0 0x1000>;
                        cpus = <&cpu_0>;
                        out-ports {
                                port {
                                        cluster0_bridge0_out_port: endpoint {
                                                remote-endpoint = <&cluster0_funnel_in_port0>;
                                        };
                                };
                        };
                        in-ports {
                                port {
                                        cluster0_bridge0_in_port: endpoint {
                                                remote-endpoint = <&cluster0_encoder0_out_port>;
                                        };
                                };
                        };
                };

...

                cluster0_funnel: funnel@d9000000 {
                        compatible = "riscv,trace-funnel";
                        reg = <0x0 0xd9000000 0x0 0x1000>;
                        cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
                        riscv,timestamp-present;
                        out-ports {
                                port {
                                        cluster0_funnel_out_port: endpoint {
                                                remote-endpoint = <&main_funnel_in_port0>;
                                        };
                                };
                        };
                        in-ports {
                                #address-cells = <1>;
                                #size-cells = <0>;

                                port@0 {
                                        reg = <0>;
                                        cluster0_funnel_in_port0: endpoint {
                                                remote-endpoint = <&cluster0_bridge0_out_port>;
                                        };
                                };

                                port@1 {
                                        reg = <1>;
                                        cluster0_funnel_in_port1: endpoint {
                                                remote-endpoint = <&cluster0_bridge1_out_port>;
                                        };
                                };

                                port@2 {
                                        reg = <2>;
                                        cluster0_funnel_in_port2: endpoint {
                                                remote-endpoint = <&cluster0_bridge2_out_port>;
                                        };
                                };

                                port@3 {
                                        reg = <3>;
                                        cluster0_funnel_in_port3: endpoint {
                                                remote-endpoint = <&cluster0_bridge3_out_port>;
                                        };
                                };
                        };
                };

                cluster1_funnel: funnel@d9010000 {
                        compatible = "riscv,trace-funnel";
                        reg = <0x0 0xd9010000 0x0 0x1000>;
                        cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
                        riscv,timestamp-present;
                        out-ports {
                                port {
                                        cluster1_funnel_out_port: endpoint {
                                                remote-endpoint = <&main_funnel_in_port1>;
                                        };
                                };
                        };
                        in-ports {
                                #address-cells = <1>;
                                #size-cells = <0>;

                                port@0 {
                                        reg = <0>;
                                        cluster1_funnel_in_port0: endpoint {
                                                remote-endpoint = <&cluster1_bridge0_out_port>;
                                        };
                                };

                                port@1 {
                                        reg = <1>;
                                        cluster1_funnel_in_port1: endpoint {
                                                remote-endpoint = <&cluster1_bridge1_out_port>;
                                        };
                                };

                                port@2 {
                                        reg = <2>;
                                        cluster1_funnel_in_port2: endpoint {
                                                remote-endpoint = <&cluster1_bridge2_out_port>;
                                        };
                                };

                                port@3 {
                                        reg = <3>;
                                        cluster1_funnel_in_port3: endpoint {
                                                remote-endpoint = <&cluster1_bridge3_out_port>;
                                        };
                                };
                        };
                };

                main_funnel: funnel@d9042000 {
                        compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
                        reg = <0x0 0xd9042000 0x0 0x1000>;
                        clocks = <&dummy_clk>;
                        clock-names = "apb_pclk";
                        out-ports {
                                port {
                                        main_funnel_out_port: endpoint {
                                                remote-endpoint = <&etf_in_port>;
                                        };
                                };
                        };
                        in-ports {
                                #address-cells = <1>;
                                #size-cells = <0>;

                                port@0 {
                                        reg = <0>;
                                        main_funnel_in_port0: endpoint {
                                                remote-endpoint = <&cluster0_funnel_out_port>;
                                        };
                                };

                                port@1 {
                                        reg = <1>;
                                        main_funnel_in_port1: endpoint {
                                                remote-endpoint = <&cluster1_funnel_out_port>;
                                        };
                                };
                        };
                };

                etf: etf@d9043000 {
                        compatible = "arm,coresight-tmc", "arm,primecell";
                        reg = <0x0 0xd9043000 0x0 0x1000>;
                        clocks = <&dummy_clk>;
                        clock-names = "apb_pclk";
                        out-ports {
                                port {
                                        etf_out_port: endpoint {
                                                remote-endpoint = <&etr_in_port>;
                                        };
                                };
                        };
                        in-ports {
                                port {
                                        etf_in_port: endpoint {
                                                remote-endpoint = <&main_funnel_out_port>;
                                        };
                                };
                        };
                };

                etr: etr@d9044000 {
                        compatible = "arm,coresight-tmc", "arm,primecell";
                        reg = <0x0 0xd9044000 0x0 0x1000>;
                        clocks = <&dummy_clk>;
                        clock-names = "apb_pclk";
                        arm,scatter-gather;
                        in-ports {
                                port {
                                        etr_in_port: endpoint {
                                                remote-endpoint = <&etf_out_port>;
                                        };
                                };
                        };
                };
        };
};

Verification case:

~ # perf list pmu
  rvtrace//                                          [Kernel PMU event]

~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
Linux
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.191 MB perf.data ]
~ # perf script
           uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
           uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
...

liangzhen (12):
  coresight: Add RISC-V support to CoreSight tracing
  coresight: Initial implementation of RISC-V trace driver
  coresight: Add RISC-V Trace Encoder driver
  coresight: Add RISC-V Trace Funnel driver
  coresight: Add RISC-V Trace ATB Bridge driver
  coresight rvtrace: Add timestamp component support for encoder and
    funnel
  coresight: Add RISC-V PMU name support
  perf tools: riscv: making rvtrace PMU listable
  perf tools: Add RISC-V trace PMU record capabilities
  perf tools: Add Nexus RISC-V Trace decoder
  perf symbols: Add RISC-V PLT entry sizes
  perf tools: Integrate RISC-V trace decoder into auxtrace

 drivers/hwtracing/Kconfig                     |    2 +
 drivers/hwtracing/coresight/Kconfig           |   46 +-
 drivers/hwtracing/coresight/Makefile          |    6 +
 drivers/hwtracing/coresight/coresight-core.c  |    8 +
 .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
 .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
 .../hwtracing/coresight/coresight-platform.c  |    1 -
 .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
 .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
 .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
 drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
 .../coresight/rvtrace-encoder-core.c          |  562 +++++++
 .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
 drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
 drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
 drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
 .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
 .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
 include/linux/coresight-pmu.h                 |    4 +
 include/linux/rvtrace.h                       |  116 ++
 tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
 tools/perf/arch/riscv/util/Build              |    2 +
 tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
 tools/perf/arch/riscv/util/pmu.c              |   20 +
 tools/perf/util/Build                         |    3 +
 tools/perf/util/auxtrace.c                    |    4 +
 tools/perf/util/auxtrace.h                    |    1 +
 tools/perf/util/nexus-rv-decoder/Build        |    1 +
 .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
 .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
 .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
 tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
 tools/perf/util/rvtrace.h                     |   40 +
 tools/perf/util/symbol-elf.c                  |    4 +
 34 files changed, 6320 insertions(+), 3 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
 create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
 create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
 create mode 100644 include/linux/rvtrace.h
 create mode 100644 tools/arch/riscv/include/asm/insn.h
 create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
 create mode 100644 tools/perf/arch/riscv/util/pmu.c
 create mode 100644 tools/perf/util/nexus-rv-decoder/Build
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
 create mode 100644 tools/perf/util/rvtrace-decoder.c
 create mode 100644 tools/perf/util/rvtrace.h

-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Zane Leung
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Enable CoreSight tracing support on RISC-V architecture by:
- Adding RISC-V to Kconfig dependencies for CoreSight
- Replacing ARM-specific memory barriers (isb, dmb) with
  RISC-V equivalents (local_flush_icache_all, __mb)
- Removing ARM-specific header dependencies:
  perf/arm_pmu.h, asm/smp_plat.h
- Adding PMU format attribute macros for cross-architecture
  support

This allows CoreSight tracing infrastructure to work on RISC-V
systems while maintaining compatibility with existing ARM/ARM64
implementations.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/Kconfig                     |  2 ++
 drivers/hwtracing/coresight/Kconfig           |  2 +-
 drivers/hwtracing/coresight/coresight-core.c  |  8 +++++++
 .../hwtracing/coresight/coresight-etm-perf.c  |  1 -
 .../hwtracing/coresight/coresight-etm-perf.h  | 21 +++++++++++++++++++
 .../hwtracing/coresight/coresight-platform.c  |  1 -
 .../hwtracing/coresight/coresight-tmc-etf.c   |  4 ++++
 .../hwtracing/coresight/coresight-tmc-etr.c   |  4 ++++
 8 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig
index 911ee977103c..167ff172dd72 100644
--- a/drivers/hwtracing/Kconfig
+++ b/drivers/hwtracing/Kconfig
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "HW tracing support"
 
+source "drivers/hwtracing/coresight/Kconfig"
+
 source "drivers/hwtracing/stm/Kconfig"
 
 source "drivers/hwtracing/intel_th/Kconfig"
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 6a4239ebb582..2b1ebe3f614d 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -4,7 +4,7 @@
 #
 menuconfig CORESIGHT
 	tristate "CoreSight Tracing Support"
-	depends on ARM || ARM64
+	depends on ARM || ARM64 || RISCV
 	depends on OF || ACPI
 	select ARM_AMBA
 	select PERF_EVENTS
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 80e26396ad0a..1ca202153cc4 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -141,7 +141,11 @@ static void coresight_set_self_claim_tag_unlocked(struct coresight_device *csdev
 {
 	csdev_access_relaxed_write32(&csdev->access, CORESIGHT_CLAIM_SELF_HOSTED,
 				     CORESIGHT_CLAIMSET);
+#if defined(__riscv)
+	local_flush_icache_all();
+#else
 	isb();
+#endif
 }
 
 void coresight_clear_self_claim_tag(struct csdev_access *csa)
@@ -158,7 +162,11 @@ void coresight_clear_self_claim_tag_unlocked(struct csdev_access *csa)
 {
 	csdev_access_relaxed_write32(csa, CORESIGHT_CLAIM_SELF_HOSTED,
 				     CORESIGHT_CLAIMCLR);
+#if defined(__riscv)
+	local_flush_icache_all();
+#else
 	isb();
+#endif
 }
 EXPORT_SYMBOL_GPL(coresight_clear_self_claim_tag_unlocked);
 
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 72017dcc3b7f..70a6aaffbf9d 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -13,7 +13,6 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/perf_event.h>
-#include <linux/perf/arm_pmu.h>
 #include <linux/percpu-defs.h>
 #include <linux/slab.h>
 #include <linux/stringhash.h>
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 24d929428633..e48c0ad46db1 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -58,6 +58,27 @@ struct cscfg_config_desc;
 #define ATTR_CFG_FLD_cc_threshold_LO		0
 #define ATTR_CFG_FLD_cc_threshold_HI		11
 
+#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi)			\
+	(lo) == (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi
+
+#define _GEN_PMU_FORMAT_ATTR(cfg, lo, hi)			\
+	__GEN_PMU_FORMAT_ATTR(cfg, lo, hi)
+
+#define GEN_PMU_FORMAT_ATTR(name)				\
+	PMU_FORMAT_ATTR(name,					\
+	_GEN_PMU_FORMAT_ATTR(ATTR_CFG_FLD_##name##_CFG,		\
+			     ATTR_CFG_FLD_##name##_LO,		\
+			     ATTR_CFG_FLD_##name##_HI))
+
+#define _ATTR_CFG_GET_FLD(attr, cfg, lo, hi)			\
+	((((attr)->cfg) >> lo) & GENMASK_ULL(hi - lo, 0))
+
+#define ATTR_CFG_GET_FLD(attr, name)				\
+	_ATTR_CFG_GET_FLD(attr,					\
+			  ATTR_CFG_FLD_##name##_CFG,		\
+			  ATTR_CFG_FLD_##name##_LO,		\
+			  ATTR_CFG_FLD_##name##_HI)
+
 /**
  * struct etm_filter - single instruction range or start/stop configuration.
  * @start_addr:	The address to start tracing on.
diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
index 0db64c5f4995..261ba6a75b86 100644
--- a/drivers/hwtracing/coresight/coresight-platform.c
+++ b/drivers/hwtracing/coresight/coresight-platform.c
@@ -14,7 +14,6 @@
 #include <linux/amba/bus.h>
 #include <linux/coresight.h>
 #include <linux/cpumask.h>
-#include <asm/smp_plat.h>
 
 #include "coresight-priv.h"
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 8882b1c4cdc0..dc366f4a5ca8 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -661,7 +661,11 @@ static int tmc_panic_sync_etf(struct coresight_device *csdev)
 	 * Make sure all previous writes are ordered,
 	 * before we mark valid
 	 */
+#if defined(__riscv)
+	__mb();
+#else
 	dmb(sy);
+#endif
 	mdata->valid = true;
 	/*
 	 * Below order need to maintained, since crc of metadata
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 4dc1defe27a5..ac379d1751e6 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1883,7 +1883,11 @@ static int tmc_panic_sync_etr(struct coresight_device *csdev)
 	 * Make sure all previous writes are ordered,
 	 * before we mark valid
 	 */
+#if defined(__riscv)
+	__mb();
+#else
 	dmb(sy);
+#endif
 	mdata->valid = true;
 	/*
 	 * Below order need to maintained, since crc of metadata
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Zane Leung
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Implement some common driver interfaces RISC-V trace
where RISC-V trace components are instantiated by a
common platform driver and a separate RISC-V trace
driver for each type of RISC-V trace component.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/coresight/Kconfig        |   8 ++
 drivers/hwtracing/coresight/Makefile       |   2 +
 drivers/hwtracing/coresight/rvtrace-core.c | 135 +++++++++++++++++++++
 include/linux/rvtrace.h                    | 116 ++++++++++++++++++
 4 files changed, 261 insertions(+)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
 create mode 100644 include/linux/rvtrace.h

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 2b1ebe3f614d..5adeaf78a080 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -280,4 +280,12 @@ config CORESIGHT_TNOC
 	  To compile this driver as a module, choose M here: the module will be
 	  called coresight-tnoc.
 
+config RVTRACE
+	bool "RISC-V Trace Support"
+	help
+	  This enables support for the RISC-V trace drivers. drivers
+	  (including Trace Encoder, Trace Funnel and  ATB Bridge) are
+	  dynamically aggregated with CoreSight trace infrastructure
+	  at run time to form a complete trace path.
+
 endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ab16d06783a5..c21a9e25e148 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -57,3 +57,5 @@ obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
 obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
 coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o
diff --git a/drivers/hwtracing/coresight/rvtrace-core.c b/drivers/hwtracing/coresight/rvtrace-core.c
new file mode 100644
index 000000000000..c74f43869d8b
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-core.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/percpu.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/rvtrace.h>
+
+int rvtrace_poll_bit(struct rvtrace_component *comp, int offset,
+		     int bit, int bitval)
+{
+	int i = RVTRACE_TIMEOUT_US;
+	u32 val;
+
+	while (i--) {
+		val = readl_relaxed(comp->base + offset);
+		if (((val >> bit) & 0x1) == bitval)
+			break;
+		udelay(1);
+	}
+
+	return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+	writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+	writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+int rvtrace_component_reset(struct rvtrace_component *comp)
+{
+	int ret;
+
+	writel_relaxed(0, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	ret = rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+			       RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0);
+	if (ret)
+		return ret;
+
+	writel_relaxed(RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+			comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1);
+}
+EXPORT_SYMBOL_GPL(rvtrace_component_reset);
+
+struct rvtrace_component *rvtrace_register_component(struct platform_device *pdev)
+{
+	int ret;
+	void __iomem *base;
+	struct device *dev = &pdev->dev;
+	struct rvtrace_component *comp;
+	struct resource *res;
+	struct device_node *node;
+	u32 impl, type, major, minor;
+
+	comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+	if (!comp) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base)) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+	comp->base = base;
+
+	comp->cpu = -1;
+	for (int i = 0; ; i++) {
+		node = of_parse_phandle(dev->of_node, "cpus", i);
+		if (!node)
+			break;
+
+		ret = of_cpu_node_to_id(node);
+		of_node_put(node);
+		if (ret >= 0 && cpu_online(ret)) {
+			comp->cpu = ret;
+			break;
+		}
+	}
+
+	if (comp->cpu < 0) {
+		dev_err(dev, "No valid CPU found in 'cpus' property\n");
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	ret = rvtrace_component_reset(comp);
+	if (ret)
+		goto err_out;
+	comp->was_reset = true;
+
+	impl = readl_relaxed(comp->base + RVTRACE_COMPONENT_IMPL_OFFSET);
+	type = (impl >> RVTRACE_COMPONENT_IMPL_TYPE_SHIFT) &
+		RVTRACE_COMPONENT_IMPL_TYPE_MASK;
+	major = (impl >> RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT) &
+		RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK;
+	minor = (impl >> RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT) &
+		RVTRACE_COMPONENT_IMPL_VERMINOR_MASK;
+
+	comp->id.type = type;
+	comp->id.version = rvtrace_component_mkversion(major, minor);
+
+	return comp;
+
+err_out:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h
new file mode 100644
index 000000000000..e7028d82f8fd
--- /dev/null
+++ b/include/linux/rvtrace.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/coresight.h>
+#include <linux/types.h>
+
+/* Control register common across all RISC-V trace components */
+#define RVTRACE_COMPONENT_CTRL_OFFSET		0x000
+#define RVTRACE_COMPONENT_CTRL_ACTIVE_MASK	0x1
+#define RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT	0
+#define RVTRACE_COMPONENT_CTRL_ENABLE_MASK	0x1
+#define RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT	1
+#define RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT	3
+
+/* Implementation register common across all RISC-V trace components */
+#define RVTRACE_COMPONENT_IMPL_OFFSET		0x004
+#define RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK	0xf
+#define RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT	0
+#define RVTRACE_COMPONENT_IMPL_VERMINOR_MASK	0xf
+#define RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT	4
+#define RVTRACE_COMPONENT_IMPL_TYPE_MASK	0xf
+#define RVTRACE_COMPONENT_IMPL_TYPE_SHIFT	8
+
+#define RVTRACE_TIMEOUT_US			100
+
+/* Possible component types defined by the RISC-V Trace Control Interface */
+enum rvtrace_component_type {
+	RVTRACE_COMPONENT_TYPE_RESV0,
+	RVTRACE_COMPONENT_TYPE_ENCODER, /* 0x1 */
+	RVTRACE_COMPONENT_TYPE_RESV2,
+	RVTRACE_COMPONENT_TYPE_RESV3,
+	RVTRACE_COMPONENT_TYPE_RESV4,
+	RVTRACE_COMPONENT_TYPE_RESV5,
+	RVTRACE_COMPONENT_TYPE_RESV6,
+	RVTRACE_COMPONENT_TYPE_RESV7,
+	RVTRACE_COMPONENT_TYPE_FUNNEL, /* 0x8 */
+	RVTRACE_COMPONENT_TYPE_RAMSINK, /* 0x9 */
+	RVTRACE_COMPONENT_TYPE_PIBSINK, /* 0xA */
+	RVTRACE_COMPONENT_TYPE_RESV11,
+	RVTRACE_COMPONENT_TYPE_RESV12,
+	RVTRACE_COMPONENT_TYPE_RESV13,
+	RVTRACE_COMPONENT_TYPE_ATBBRIDGE, /* 0xE */
+	RVTRACE_COMPONENT_TYPE_RESV15,
+	RVTRACE_COMPONENT_TYPE_MAX
+};
+
+/* Encoding/decoding macros for RISC-V trace component version */
+#define rvtrace_component_version_major(__version)	\
+	(((__version) >> 16) & 0xffff)
+#define rvtrace_component_version_minor(__version)	\
+	((__version) & 0xffff)
+#define rvtrace_component_mkversion(__major, __minor)	\
+	((((__major) & 0xffff) << 16) |	((__minor) & 0xffff))
+
+/**
+ * struct rvtrace_component_id - Details to identify or match a RISC-V trace component
+ * @type:      Type of the component
+ * @version:   Version of the component
+ * @data:      Data pointer for driver use
+ */
+struct rvtrace_component_id {
+	enum rvtrace_component_type type;
+	u32 version;
+	void *data;
+};
+
+/**
+ * struct rvtrace_component - Representation of a RISC-V trace component
+ * base:             Memory mapped base address for the component
+ * id:               Details to match the component
+ * dev:              Device instance
+ * cpu:              The cpu this component is affined to
+ * was_reset:        Flag showing whether RISC-V trace driver was reset successfully
+ */
+struct rvtrace_component {
+	void __iomem *base;
+	struct rvtrace_component_id id;
+	struct device *dev;
+	int cpu;
+	bool was_reset;
+};
+
+struct component_enable_arg {
+	struct rvtrace_component *comp;
+	int rc;
+};
+
+struct rvtrace_component *rvtrace_register_component(struct platform_device *pdev);
+
+int rvtrace_poll_bit(struct rvtrace_component *comp, int offset,
+		     int bit, int bitval);
+
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+int rvtrace_component_reset(struct rvtrace_component *comp);
+
+static inline void *rvtrace_component_data(struct rvtrace_component *comp)
+{
+	return comp->id.data;
+}
+
+static inline int rvtrace_comp_is_empty(struct rvtrace_component *comp)
+{
+	return rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT, 1);
+}
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Zane Leung
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add initial implementation of RISC-V trace encoder
driver. The encoder is defined in the RISC-V Trace
Control Interface specification.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/coresight/Kconfig           |  12 +
 drivers/hwtracing/coresight/Makefile          |   2 +
 .../coresight/rvtrace-encoder-core.c          | 524 ++++++++++++++++++
 .../coresight/rvtrace-encoder-sysfs.c         | 313 +++++++++++
 drivers/hwtracing/coresight/rvtrace-encoder.h | 143 +++++
 5 files changed, 994 insertions(+)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 5adeaf78a080..3e46728f2482 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -288,4 +288,16 @@ config RVTRACE
 	  dynamically aggregated with CoreSight trace infrastructure
 	  at run time to form a complete trace path.
 
+config RVTRACE_ENCODER
+	tristate "RISCV Trace Encoder driver"
+	depends on RVTRACE
+	help
+	  This driver provides support for the Trace Encoder module, tracing
+	  the instructions that a processor is executing. This is primarily
+	  useful for instruction level tracing. Depending on the implemented
+	  version data tracing may also be available.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rvtrace-encoder.
+
 endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index c21a9e25e148..9a526b1fb95a 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -59,3 +59,5 @@ coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
 obj-$(CONFIG_RVTRACE) += rvtrace.o
 rvtrace-y := rvtrace-core.o
+obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
+rvtrace-encoder-y := rvtrace-encoder-core.o rvtrace-encoder-sysfs.o
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-core.c b/drivers/hwtracing/coresight/rvtrace-encoder-core.c
new file mode 100644
index 000000000000..149a3cd97602
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-encoder-core.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/bitfield.h>
+#include <linux/smp.h>
+#include <linux/coresight.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rvtrace.h>
+
+#include "rvtrace-encoder.h"
+#include "coresight-etm-perf.h"
+
+static int boot_enable;
+module_param_named(boot_enable, boot_enable, int, 0444);
+
+static struct rvtrace_component *rvtrace_cpu_encoder[NR_CPUS];
+
+static int encoder_cpu_id(struct coresight_device *csdev)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+
+	return comp->cpu;
+}
+
+static void encoder_set_config(struct rvtrace_component *comp)
+{
+	u32 val;
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct device *dev = &encoder_data->csdev->dev;
+	struct encoder_config *config = &encoder_data->config;
+
+	/* Configure Trace Encoder features register */
+	val = (FIELD_PREP(RVTRACE_ENCODER_INST_NO_ADDR_DIFF, config->inst_na_diff) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_NO_TRAP_ADDR, config->inst_nt_addr) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EN_SEQUENTIAL_JUMP, config->seq_jump) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EN_IMPLICIT_RETURN, config->impl_ret) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EN_BRANCH_PREDICTION, config->branch_pred) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_IMPLICIT_RETURN_MODE, config->impl_ret_mode) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EN_REPEQTED_HISTORT, config->rep_hist) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EN_ALL_JUMPS, config->all_jumps) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_EXTEND_ADDR_MSB, config->ext_msb) |
+		   FIELD_PREP(RVTRACE_ENCODER_SRCID, config->srcid) |
+		   FIELD_PREP(RVTRACE_ENCODER_SRCBITS, config->srcb));
+
+	writel_relaxed(val, comp->base + RVTRACE_ENCODER_INST_FTRS_OFFSET);
+
+	/* Check Trace Encoder features register WARL bits could be set */
+	val = readl_relaxed(comp->base + RVTRACE_ENCODER_INST_FTRS_OFFSET);
+	if (BMVAL(val, 0, 0) != config->inst_na_diff) {
+		dev_warn(dev, "trTeInstNoAddrDiff %#x is not supported\n",
+			 config->inst_na_diff);
+		config->inst_na_diff = BMVAL(val, 0, 0);
+	}
+
+	if (BMVAL(val, 1, 1) != config->inst_nt_addr) {
+		dev_warn(dev, "trTeInstNoTrapAddr %#x is not supported\n",
+			 config->inst_nt_addr);
+		config->inst_nt_addr = BMVAL(val, 1, 1);
+	}
+
+	if (BMVAL(val, 2, 2) != config->seq_jump) {
+		dev_warn(dev, "trTeInstEnSequentialJump %#x is not supported\n",
+			 config->seq_jump);
+		config->seq_jump = BMVAL(val, 2, 2);
+	}
+
+	if (BMVAL(val, 3, 3) != config->impl_ret) {
+		dev_warn(dev, "trTeInstEnImplicitReturn %#x is not supported\n",
+			 config->impl_ret);
+		config->impl_ret = BMVAL(val, 3, 3);
+	}
+
+	if (BMVAL(val, 4, 4) != config->branch_pred) {
+		dev_warn(dev, "trTeInstEnBranchPrediction %#x is not supported\n",
+			 config->branch_pred);
+		config->branch_pred = BMVAL(val, 4, 4);
+	}
+
+	if (BMVAL(val, 5, 5) != config->jump_target_cache) {
+		dev_warn(dev, "trTeInstEnJumpTargetCache %#x is not supported\n",
+			 config->jump_target_cache);
+		config->jump_target_cache = BMVAL(val, 5, 5);
+	}
+
+	if (BMVAL(val, 6, 7) != config->impl_ret_mode) {
+		dev_warn(dev, "trTeInstImplicitReturnMode %#x is not supported\n",
+			 config->impl_ret_mode);
+		config->impl_ret_mode = BMVAL(val, 6, 7);
+	}
+
+	if (BMVAL(val, 8, 8) != config->rep_hist) {
+		dev_warn(dev, "trTeInstEnRepeatedHistory %#x is not supported\n",
+			 config->rep_hist);
+		config->rep_hist = BMVAL(val, 8, 8);
+	}
+
+	if (BMVAL(val, 9, 9) != config->all_jumps) {
+		dev_warn(dev, "trTeInstEnAllJumps %#x is not supported\n",
+			 config->all_jumps);
+		config->all_jumps = BMVAL(val, 9, 9);
+	}
+
+	if (BMVAL(val, 10, 10) != config->ext_msb) {
+		dev_warn(dev, " trTeInstExtendAddrMSB %#x is not supported\n",
+			 config->ext_msb);
+		config->ext_msb = BMVAL(val, 10, 10);
+	}
+
+	if (BMVAL(val, 16, 27) != config->srcid) {
+		dev_warn(dev, "trTeSrcID %#x is not supported\n",
+			 config->srcid);
+		config->srcid = BMVAL(val, 16, 27);
+	}
+
+	if (BMVAL(val, 28, 31) != config->srcb) {
+		dev_warn(dev, "trTeSrcBits %#x is not supported\n",
+			 config->srcb);
+		config->srcb = BMVAL(val, 28, 31);
+	}
+
+	/* Configure Trace Encoder control register */
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= (FIELD_PREP(RVTRACE_ENCODER_INSTMODE, ENCODER_INSTMODE_HTM) |
+		   FIELD_PREP(RVTRACE_ENCODER_CONTEXT, config->context) |
+		   FIELD_PREP(RVTRACE_ENCODER_INSTTRIGEN, config->inst_trigger) |
+		   FIELD_PREP(RVTRACE_ENCODER_INST_STALL_EN, config->inst_stall) |
+		   FIELD_PREP(RVTRACE_ENCODER_INHBSRC, config->inhb_src) |
+		   FIELD_PREP(RVTRACE_ENCODER_INSTSYNC_MODE, config->inst_syncmode) |
+		   FIELD_PREP(RVTRACE_ENCODER_INSTSYNC_MAX, config->inst_syncmax));
+
+
+	writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+
+	/* Check Trace Encoder control register WARL bits could be set */
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+
+	if (BMVAL(val, 9, 9) != config->context) {
+		dev_warn(dev, "trTeContext %#x is not supported\n",
+			 config->context);
+		config->context = BMVAL(val, 9, 9);
+	}
+
+	if (BMVAL(val, 11, 11) != config->inst_trigger) {
+		dev_warn(dev, "trTeInstTrigEnable %#x is not supported\n",
+			 config->inst_trigger);
+		config->inst_trigger = BMVAL(val, 11, 11);
+	}
+
+	if (BMVAL(val, 13, 13) != config->inst_stall) {
+		dev_warn(dev, "trTeInstStallEna %#x is not supported\n",
+			 config->inst_stall);
+		config->inst_stall = BMVAL(val, 13, 13);
+	}
+
+	if (BMVAL(val, 15, 15) != config->inhb_src) {
+		dev_warn(dev, "trTeInhibitSrc %#x is not supported\n",
+			 config->inhb_src);
+		config->inhb_src = BMVAL(val, 15, 15);
+	}
+
+	if (BMVAL(val, 20, 23) != config->inst_syncmax) {
+		dev_warn(dev, "trTeInstSyncMax %#x is not supported\n",
+			 config->inst_syncmax);
+		config->inst_syncmax = BMVAL(val, 20, 23);
+	}
+}
+
+static int encoder_enable_hw(struct rvtrace_component *comp)
+{
+	u32 val;
+	int ret;
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (!comp->was_reset) {
+		ret = rvtrace_component_reset(comp);
+		if (ret)
+			goto done;
+	}
+
+	encoder_set_config(comp);
+
+	ret = rvtrace_enable_component(comp);
+	if (ret)
+		goto done;
+
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= RVTRACE_ENCODER_ITRACE;
+	writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	ret = rvtrace_poll_bit(comp, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 1);
+
+done:
+	dev_dbg(&encoder_data->csdev->dev, "cpu: %d enable smp call done: %d\n",
+		comp->cpu, ret);
+	return ret;
+}
+
+static void encoder_enable_hw_smp_call(void *info)
+{
+	struct component_enable_arg *arg = info;
+
+	if (WARN_ON(!arg))
+		return;
+	arg->rc = encoder_enable_hw(arg->comp);
+}
+
+static int encoder_enable_perf(struct coresight_device *csdev)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(comp->cpu != smp_processor_id()))
+		return -EINVAL;
+
+	return encoder_enable_hw(comp);
+}
+
+static int encoder_enable_sysfs(struct coresight_device *csdev)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct component_enable_arg arg = { };
+	int ret;
+
+	spin_lock(&encoder_data->spinlock);
+
+	/*
+	 * Executing encoder_enable_hw on the cpu whose trace encoder is being
+	 * enabled ensures that register writes occur when cpu is powered.
+	 */
+	arg.comp = comp;
+	ret = smp_call_function_single(comp->cpu,
+				       encoder_enable_hw_smp_call, &arg, 1);
+	if (!ret)
+		ret = arg.rc;
+	if (!ret)
+		encoder_data->sticky_enable = true;
+
+	spin_unlock(&encoder_data->spinlock);
+
+	if (!ret)
+		dev_dbg(&csdev->dev, "Trace Encoder tracing enabled\n");
+	return ret;
+}
+
+static int encoder_enable(struct coresight_device *csdev, struct perf_event *event,
+			  enum cs_mode mode, __maybe_unused struct coresight_path *path)
+{
+	int ret;
+
+	if (!coresight_take_mode(csdev, mode)) {
+		/* Someone is already using the tracer */
+		return -EBUSY;
+	}
+
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		ret = encoder_enable_sysfs(csdev);
+		break;
+	case CS_MODE_PERF:
+		ret = encoder_enable_perf(csdev);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	/* The tracer didn't start */
+	if (ret)
+		coresight_set_mode(csdev, CS_MODE_DISABLED);
+
+	return ret;
+}
+
+static void encoder_disable_hw(struct rvtrace_component *comp)
+{
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (rvtrace_disable_component(comp))
+		dev_err(&encoder_data->csdev->dev,
+			"timeout while waiting for Trace Encoder become disabled\n");
+
+	if (rvtrace_comp_is_empty(comp))
+		dev_err(&encoder_data->csdev->dev,
+			"timeout while waiting for all generated trace have been emitted\n");
+
+	dev_dbg(&encoder_data->csdev->dev, "cpu: %d disable smp call done\n", comp->cpu);
+}
+
+static void encoder_disable_sysfs_smp_call(void *info)
+{
+	struct rvtrace_component *comp = info;
+
+	encoder_disable_hw(comp);
+}
+
+static void encoder_disable_sysfs(struct coresight_device *csdev)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	/*
+	 * Taking hotplug lock here protects from clocks getting disabled
+	 * with tracing being left on (crash scenario) if user disable occurs
+	 * after cpu online mask indicates the cpu is offline but before the
+	 * DYING hotplug callback is serviced by the trace encoder driver.
+	 */
+	cpus_read_lock();
+	spin_lock(&encoder_data->spinlock);
+
+	/*
+	 * Executing encoder_disable_hw on the cpu whose trace encoder is being
+	 * disabled ensures that register writes occur when cpu is powered.
+	 */
+	smp_call_function_single(comp->cpu, encoder_disable_sysfs_smp_call, comp, 1);
+
+	spin_unlock(&encoder_data->spinlock);
+	cpus_read_unlock();
+
+	dev_dbg(&csdev->dev, "Trace Encoder tracing disabled\n");
+}
+
+static void encoder_disable_perf(struct coresight_device *csdev)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(comp->cpu != smp_processor_id()))
+		return;
+
+	encoder_disable_hw(comp);
+}
+
+static void encoder_disable(struct coresight_device *csdev,
+			    struct perf_event *event)
+{
+	enum cs_mode mode;
+
+	/*
+	 * For as long as the tracer isn't disabled another entity can't
+	 * change its status.  As such we can read the status here without
+	 * fearing it will change under us.
+	 */
+	mode = coresight_get_mode(csdev);
+
+	switch (mode) {
+	case CS_MODE_DISABLED:
+		break;
+	case CS_MODE_SYSFS:
+		encoder_disable_sysfs(csdev);
+		break;
+	case CS_MODE_PERF:
+		encoder_disable_perf(csdev);
+		break;
+	default:
+		WARN_ON_ONCE(mode);
+		return;
+	}
+
+	if (mode)
+		coresight_set_mode(csdev, CS_MODE_DISABLED);
+}
+
+static const struct coresight_ops_source encoder_source_ops = {
+	.cpu_id		= encoder_cpu_id,
+	.enable         = encoder_enable,
+	.disable        = encoder_disable,
+};
+
+static const struct coresight_ops encoder_cs_ops = {
+	.source_ops     = &encoder_source_ops,
+};
+
+void encoder_set_default(struct rvtrace_component *comp)
+{
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (WARN_ON_ONCE(!encoder_data))
+		return;
+
+	struct encoder_config *config = &encoder_data->config;
+
+	/* Enable sending trace messages/fields with scontext/mcontext values
+	 * and/or privilege levels
+	 */
+	config->context = true;
+
+	/* Allows trTeInstTracing to be set or cleared by Trace-on and Trace-
+	 * off signals generated by the corresponding trigger module.
+	 */
+	config->inst_trigger = true;
+
+	/* Enable periodic instruction trace synchronization */
+	config->inst_syncmode = ENCODER_SYNCMODE_CLOCK;
+	config->inst_syncmax = 0x6;
+
+	/* trace source ID*/
+	config->srcid = encoder_cpu_id(encoder_data->csdev);
+	config->srcb = 0xc;
+}
+
+static int encoder_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata;
+	struct encoder_data *encoder_data;
+	struct rvtrace_component *comp;
+	struct coresight_desc desc = { 0 };
+
+	comp = rvtrace_register_component(pdev);
+	if (IS_ERR(comp))
+		return PTR_ERR(comp);
+
+	encoder_data = devm_kzalloc(dev, sizeof(*encoder_data), GFP_KERNEL);
+	if (!encoder_data)
+		return -ENOMEM;
+
+	spin_lock_init(&encoder_data->spinlock);
+
+	pdata = coresight_get_platform_data(dev);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	pdev->dev.platform_data = pdata;
+
+	platform_set_drvdata(pdev, comp);
+
+	desc.name = devm_kasprintf(dev, GFP_KERNEL, "encoder%d", comp->cpu);
+	if (!desc.name)
+		return -ENOMEM;
+
+	desc.access = CSDEV_ACCESS_IOMEM(comp->base);
+	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
+	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
+	desc.ops = &encoder_cs_ops;
+	desc.pdata = pdata;
+	desc.dev = dev;
+	desc.groups = trace_encoder_groups;
+	encoder_data->csdev = coresight_register(&desc);
+	if (IS_ERR(encoder_data->csdev))
+		return PTR_ERR(encoder_data->csdev);
+
+	ret = etm_perf_symlink(encoder_data->csdev, true);
+	if (ret) {
+		coresight_unregister(encoder_data->csdev);
+		return ret;
+	}
+
+	comp->id.data = encoder_data;
+
+	rvtrace_cpu_encoder[comp->cpu] = comp;
+
+	encoder_set_default(comp);
+
+	dev_info(dev, "CPU%d: Trace Encoder initialized\n", comp->cpu);
+
+	if (boot_enable) {
+		coresight_enable_sysfs(encoder_data->csdev);
+		encoder_data->boot_enable = true;
+	}
+
+	return 0;
+}
+
+static void clear_encodata(void *info)
+{
+	int cpu = *(int *)info;
+
+	rvtrace_cpu_encoder[cpu]->id.data = NULL;
+	rvtrace_cpu_encoder[cpu] = NULL;
+}
+
+static void encoder_remove(struct platform_device *pdev)
+{
+	struct rvtrace_component *comp = platform_get_drvdata(pdev);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	etm_perf_symlink(encoder_data->csdev, false);
+
+	/*
+	 * Taking hotplug lock here to avoid racing between encoder_remove and
+	 * CPU hotplug call backs.
+	 */
+	cpus_read_lock();
+	/*
+	 * The readers for encodata[] are CPU hotplug call backs
+	 * and PM notification call backs. Change encodata[i] on
+	 * CPU i ensures these call backs has consistent view
+	 * inside one call back function.
+	 */
+	if (smp_call_function_single(comp->cpu, clear_encodata, &comp->cpu, 1)) {
+		rvtrace_cpu_encoder[comp->cpu]->id.data = NULL;
+		rvtrace_cpu_encoder[comp->cpu] = NULL;
+	}
+
+	cpus_read_unlock();
+
+	coresight_unregister(encoder_data->csdev);
+}
+
+static const struct of_device_id encoder_match[] = {
+	{.compatible = "riscv,trace-encoder"},
+	{},
+};
+
+static struct platform_driver encoder_driver = {
+	.probe = encoder_probe,
+	.remove = encoder_remove,
+	.driver = {
+		.name = "trace-encoder",
+		.of_match_table = encoder_match,
+	},
+};
+
+module_platform_driver(encoder_driver);
+
+MODULE_DESCRIPTION("RISC-V Trace Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
new file mode 100644
index 000000000000..c89af4cb591a
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/sysfs.h>
+#include <linux/rvtrace.h>
+#include "rvtrace-encoder.h"
+#include "coresight-priv.h"
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+
+	val = comp->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(cpu);
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	spin_lock(&encoder_data->spinlock);
+
+	if (val) {
+		encoder_set_default(comp);
+		if (rvtrace_component_reset(comp)) {
+			comp->was_reset = false;
+			spin_unlock(&encoder_data->spinlock);
+			return -EINVAL;
+		}
+	}
+
+	spin_unlock(&encoder_data->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static struct attribute *trace_encoder_attrs[] = {
+	&dev_attr_cpu.attr,
+	&dev_attr_reset.attr,
+	NULL,
+};
+
+static struct attribute *trace_encoder_mgmt_attrs[] = {
+	coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET),
+	coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET),
+	coresight_simple_reg32(features, RVTRACE_ENCODER_INST_FTRS_OFFSET),
+	NULL,
+};
+
+#define encoder_simple_rw(name)						    \
+static ssize_t name##_show(struct device *dev,				    \
+			   struct device_attribute *attr, char *buf)	    \
+{									    \
+	unsigned long val;						    \
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);	    \
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);   \
+	struct encoder_config *config = &encoder_data->config;		    \
+									    \
+	val = config->name;						    \
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);		    \
+}									    \
+									    \
+static ssize_t name##_store(struct device *dev,				    \
+			    struct device_attribute *attr,		    \
+			    const char *buf, size_t size)		    \
+{									    \
+	unsigned long val;						    \
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);	    \
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);   \
+	struct encoder_config *config = &encoder_data->config;		    \
+									    \
+	if (kstrtoul(buf, 16, &val))					    \
+		return -EINVAL;						    \
+									    \
+	spin_lock(&encoder_data->spinlock);				    \
+	config->name = val;						    \
+	spin_unlock(&encoder_data->spinlock);				    \
+									    \
+	return size;							    \
+}									    \
+static DEVICE_ATTR_RW(name)
+
+static const char *const instmodes_str[] = {
+	[ENCODER_INSTMODE_OFF]	    = "off",
+	[ENCODER_INSTMODE_RESV1]    = "resv1",
+	[ENCODER_INSTMODE_BTM]	    = "btm",
+	[ENCODER_INSTMODE_RESV4]    = "resv4",
+	[ENCODER_INSTMODE_RESV5]    = "resv5",
+	[ENCODER_INSTMODE_HTM]	    = "htm",
+	[ENCODER_INSTMODE_RESV7]    = "resv7",
+};
+
+static ssize_t instmode_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val = BMVAL(val, 4, 6);
+
+	return sysfs_emit(buf, "%s\n", instmodes_str[val]);
+}
+static DEVICE_ATTR_RO(instmode);
+
+static const char *const inst_syncmodes_str[] = {
+	[ENCODER_SYNCMODE_OFF]		= "off",
+	[ENCODER_SYNCMODE_MESSAGES]	= "messages",
+	[ENCODER_SYNCMODE_CLOCK]	= "clock",
+	[ENCODER_SYNCMODE_INSTRUCTION]	= "instruction"
+};
+
+static ssize_t inst_syncmodes_available_show(struct device *dev,
+					     struct device_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%s %s %s %s\n",
+			    inst_syncmodes_str[ENCODER_SYNCMODE_OFF],
+			    inst_syncmodes_str[ENCODER_SYNCMODE_MESSAGES],
+			    inst_syncmodes_str[ENCODER_SYNCMODE_CLOCK],
+			    inst_syncmodes_str[ENCODER_SYNCMODE_INSTRUCTION]);
+}
+static DEVICE_ATTR_RO(inst_syncmodes_available);
+
+static ssize_t inst_syncmode_preferred_show(struct device *dev,
+					    struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct encoder_config *config = &encoder_data->config;
+
+	return sysfs_emit(buf, "%s\n", inst_syncmodes_str[config->inst_syncmode]);
+}
+
+static ssize_t inst_syncmode_preferred_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t size)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct encoder_config *config = &encoder_data->config;
+
+	if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_OFF]))
+		config->inst_syncmode = ENCODER_SYNCMODE_OFF;
+	else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_MESSAGES]))
+		config->inst_syncmode = ENCODER_SYNCMODE_MESSAGES;
+	else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_CLOCK]))
+		config->inst_syncmode = ENCODER_SYNCMODE_CLOCK;
+	else if (sysfs_streq(buf, inst_syncmodes_str[ENCODER_SYNCMODE_INSTRUCTION]))
+		config->inst_syncmode = ENCODER_SYNCMODE_INSTRUCTION;
+	else
+		return -EINVAL;
+
+	return size;
+}
+static DEVICE_ATTR_RW(inst_syncmode_preferred);
+
+static ssize_t format_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+
+	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	val = BMVAL(val, 24, 26);
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(format);
+
+encoder_simple_rw(context);
+encoder_simple_rw(inst_trigger);
+encoder_simple_rw(inst_stall);
+encoder_simple_rw(inhb_src);
+encoder_simple_rw(inst_syncmax);
+
+static struct attribute *trace_encoder_control_attrs[] = {
+	&dev_attr_instmode.attr,
+	&dev_attr_context.attr,
+	&dev_attr_inst_trigger.attr,
+	&dev_attr_inst_stall.attr,
+	&dev_attr_inhb_src.attr,
+	&dev_attr_inst_syncmodes_available.attr,
+	&dev_attr_inst_syncmode_preferred.attr,
+	&dev_attr_inst_syncmax.attr,
+	&dev_attr_format.attr,
+	NULL,
+};
+
+static const char *const inst_implicit_return_modes_str[] = {
+	[ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED]	= "off",
+	[ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING]	= "simple",
+	[ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS]	= "partial",
+	[ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS]	= "full"
+};
+
+static ssize_t inst_implicit_return_modes_available_show(struct device *dev,
+							 struct device_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%s %s %s %s\n",
+			inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED],
+			inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING],
+			inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS],
+			inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS]);
+}
+static DEVICE_ATTR_RO(inst_implicit_return_modes_available);
+
+
+static ssize_t inst_implicit_return_mode_preferred_show(struct device *dev,
+					    struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct encoder_config *config = &encoder_data->config;
+
+	return sysfs_emit(buf, "%s\n", inst_implicit_return_modes_str[config->impl_ret_mode]);
+}
+
+static ssize_t inst_implicit_return_mode_preferred_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t size)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+	struct encoder_config *config = &encoder_data->config;
+
+	if (sysfs_streq(buf,
+		inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED])) {
+		config->impl_ret_mode = ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED;
+	} else if (sysfs_streq(buf,
+		inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING])) {
+		config->impl_ret_mode = ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING;
+	} else if (sysfs_streq(buf,
+		inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS])) {
+		config->impl_ret_mode = ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS;
+	} else if (sysfs_streq(buf,
+		inst_implicit_return_modes_str[ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS])) {
+		config->impl_ret_mode = ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS;
+	} else {
+		return -EINVAL;
+	}
+	return size;
+}
+static DEVICE_ATTR_RW(inst_implicit_return_mode_preferred);
+
+encoder_simple_rw(inst_na_diff);
+encoder_simple_rw(inst_nt_addr);
+encoder_simple_rw(seq_jump);
+encoder_simple_rw(impl_ret);
+encoder_simple_rw(branch_pred);
+encoder_simple_rw(jump_target_cache);
+encoder_simple_rw(rep_hist);
+encoder_simple_rw(all_jumps);
+encoder_simple_rw(ext_msb);
+encoder_simple_rw(srcid);
+encoder_simple_rw(srcb);
+
+static struct attribute *trace_encoder_features_attrs[] = {
+	&dev_attr_inst_na_diff.attr,
+	&dev_attr_inst_nt_addr.attr,
+	&dev_attr_seq_jump.attr,
+	&dev_attr_impl_ret.attr,
+	&dev_attr_branch_pred.attr,
+	&dev_attr_jump_target_cache.attr,
+	&dev_attr_inst_implicit_return_modes_available.attr,
+	&dev_attr_inst_implicit_return_mode_preferred.attr,
+	&dev_attr_rep_hist.attr,
+	&dev_attr_all_jumps.attr,
+	&dev_attr_ext_msb.attr,
+	&dev_attr_srcid.attr,
+	&dev_attr_srcb.attr,
+	NULL,
+};
+
+static const struct attribute_group trace_encoder_group = {
+	.attrs = trace_encoder_attrs,
+};
+
+static const struct attribute_group trace_encoder_mgmt_group = {
+	.attrs = trace_encoder_mgmt_attrs,
+	.name = "mgmt",
+};
+
+static const struct attribute_group trace_encoder_control_group = {
+	.attrs = trace_encoder_control_attrs,
+	.name = "control",
+};
+
+static const struct attribute_group trace_encoder_features_group = {
+	.attrs = trace_encoder_features_attrs,
+	.name = "features",
+};
+
+const struct attribute_group *trace_encoder_groups[] = {
+	&trace_encoder_group,
+	&trace_encoder_mgmt_group,
+	&trace_encoder_control_group,
+	&trace_encoder_features_group,
+	NULL,
+};
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder.h b/drivers/hwtracing/coresight/rvtrace-encoder.h
new file mode 100644
index 000000000000..44f2066ad869
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-encoder.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#ifndef _RVTRACE_ENCODER_H
+#define _RVTRACE_ENCODER_H
+
+#include <asm/local.h>
+#include <linux/spinlock.h>
+#include "coresight-priv.h"
+
+/* Trace Encoder Control Register */
+#define RVTRACE_ENCODER_ITRACE				    BIT(2)
+#define RVTRACE_ENCODER_INSTMODE			    GENMASK(6, 4)
+#define RVTRACE_ENCODER_CONTEXT				BIT(9)
+#define RVTRACE_ENCODER_INSTTRIGEN			BIT(11)
+#define RVTRACE_ENCODER_INST_STALL_OR_OVERFLOW		BIT(12)
+#define RVTRACE_ENCODER_INST_STALL_EN			    BIT(13)
+#define RVTRACE_ENCODER_INHBSRC				    BIT(15)
+#define RVTRACE_ENCODER_INSTSYNC_MODE			    GENMASK(17, 16)
+#define RVTRACE_ENCODER_INSTSYNC_MAX			    GENMASK(23, 20)
+
+#define RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT		    2
+
+/* Trace Encoder Implementation Register */
+
+/* Trace Instruction Features Register */
+#define RVTRACE_ENCODER_INST_FTRS_OFFSET		    0x008
+#define RVTRACE_ENCODER_INST_NO_ADDR_DIFF		    BIT(0)
+#define RVTRACE_ENCODER_INST_NO_TRAP_ADDR		    BIT(1)
+#define RVTRACE_ENCODER_INST_EN_SEQUENTIAL_JUMP		    BIT(2)
+#define RVTRACE_ENCODER_INST_EN_IMPLICIT_RETURN		    BIT(3)
+#define RVTRACE_ENCODER_INST_EN_BRANCH_PREDICTION	    BIT(4)
+#define RVTRACE_ENCODER_INST_JUMP_TARGET_CACHE		    BIT(5)
+#define RVTRACE_ENCODER_INST_IMPLICIT_RETURN_MODE	    GENMASK(7, 6)
+#define RVTRACE_ENCODER_INST_EN_REPEQTED_HISTORT	    BIT(8)
+#define RVTRACE_ENCODER_INST_EN_ALL_JUMPS		    BIT(9)
+#define RVTRACE_ENCODER_INST_EXTEND_ADDR_MSB		    BIT(10)
+#define RVTRACE_ENCODER_SRCID				    GENMASK(27, 16)
+#define RVTRACE_ENCODER_SRCBITS				    GENMASK(31, 28)
+
+enum encoder_format {
+	ENCODER_FORMAT_ETRACE,
+	ENCODER_FORMAT_NTRACE,
+	ENCODER_FORMAT_UNKNOWN = 7
+};
+
+enum encoder_instmode {
+	ENCODER_INSTMODE_OFF,
+	ENCODER_INSTMODE_RESV1,
+	ENCODER_INSTMODE_RESV2,
+	ENCODER_INSTMODE_BTM,
+	ENCODER_INSTMODE_RESV4,
+	ENCODER_INSTMODE_RESV5,
+	ENCODER_INSTMODE_HTM,
+	ENCODER_INSTMODE_RESV7
+};
+
+enum encoder_syncmode {
+	ENCODER_SYNCMODE_OFF,
+	ENCODER_SYNCMODE_MESSAGES,
+	ENCODER_SYNCMODE_CLOCK,
+	ENCODER_SYNCMODE_INSTRUCTION
+};
+
+enum encoder_implicit_return_mode {
+	ENCODER_IMPLICITRETURNMODE_NOT_SUPPORTED,
+	ENCODER_IMPLICITRETURNMODE_SIMPLE_COUNTING,
+	ENCODER_IMPLICITRETURNMODE_PARTIAL_ADDRESS,
+	ENCODER_IMPLICITRETURNMODE_FULL_ADDRESS
+};
+
+/**
+ * struct encoder_config - configuration information related to an trace encoder
+ * @context:		Controls sending trace messages/fields with scontext/mcontext
+ *			values and/or privilege levels.
+ * @inst_trig:		Controls trTeInstTracing to be set or cleared by Trace-on and
+ *			Trace-off signals generated by the corresponding trigger module.
+ * @inst_stall:		Controls whether hart is stalled until TE can send a message.
+ * @inhb_src:		Controls whether include source field in trace messages/packets.
+ * @inst_syncmode:	Periodic instruction trace synchronization message/packet
+ *			generation mechanism.
+ * @inst_syncmax:	The maximum interval between instruction trace synchronization
+ *			messages/packets.
+ * @trc_fmt:		Trace recording/protocol format.
+ * @inst_na_diff:	Controls whether trace messages/packets always carry a full address.
+ * @inst_nt_addr:	Controls whether include trap handler address in trap messages/packets.
+ * @seq_jump:		Controls the sequential jump optimization.
+ * @impl_ret:		Controls the implicit return optimization.
+ * @branch_pred:	Branch Predictor based compression.
+ * @jump_target_cache:	Jump Target Cache based compression.
+ * @impl_ret_mode:	Controls how the decoder is handling stack of return addresses.
+ * @rep_hist:		Controls the repeated history optimization.
+ * @all_jumps:		Controls the emitting of trace message or add history/map bit for
+ *			direct unconditional/inferable control flow changes.
+ * @ext_msb:		Controls the extended handing of MSB address bits.
+ * @srcid:		Trace source ID assigned to this trace encoder.
+ * @srcb:		The number of bits in the trace source field, unless disabled by
+ *			inhibit_src.
+ */
+struct encoder_config {
+	bool				    context;
+	bool				    inst_trigger;
+	bool				    inst_stall;
+	bool				    inhb_src;
+	u8				    inst_syncmode;
+	u8				    inst_syncmax;
+	bool				    inst_na_diff;
+	bool				    inst_nt_addr;
+	bool				    seq_jump;
+	bool				    impl_ret;
+	bool				    branch_pred;
+	bool				    jump_target_cache;
+	u8				    impl_ret_mode;
+	bool				    rep_hist;
+	bool				    all_jumps;
+	bool				    ext_msb;
+	u16				    srcid;
+	u8				    srcb;
+};
+
+/**
+ * struct encoder_drvdata - specifics associated to an Trace Encoder component
+ * @csdev:	    Component vitals needed by the framework.
+ * @spinlock:	    Only one at a time pls.
+ * @sticky_enable:  True if trace encoder base configuration has been done.
+ * @boot_enable:    True if we should start tracing at boot time.
+ * @config:	    Structure holding configuration parameters.
+ */
+
+struct encoder_data {
+	struct coresight_device		*csdev;
+	spinlock_t			spinlock;
+	bool				sticky_enable;
+	bool				boot_enable;
+	struct encoder_config		config;
+};
+
+extern const struct attribute_group *trace_encoder_groups[];
+void encoder_set_default(struct rvtrace_component *comp);
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (2 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Zane Leung
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add initial implementation of RISC-V trace funnel
driver. The funnel is defined in the RISC-V Trace
Control Interface specification.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/coresight/Kconfig          |  12 +
 drivers/hwtracing/coresight/Makefile         |   1 +
 drivers/hwtracing/coresight/rvtrace-funnel.c | 244 +++++++++++++++++++
 drivers/hwtracing/coresight/rvtrace-funnel.h |  31 +++
 4 files changed, 288 insertions(+)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 3e46728f2482..8381a84e2699 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -288,9 +288,21 @@ config RVTRACE
 	  dynamically aggregated with CoreSight trace infrastructure
 	  at run time to form a complete trace path.
 
+config RVTRACE_FUNNEL
+	tristate "RISCV Trace Funnel driver"
+	depends on RVTRACE
+	help
+	  his driver provides support for the Trace Funnel driver. The Funnel
+	  aggregates the trace from each of its inputs and sends the combined
+	  trace stream to its designated Trace Sink or ATB Bridge.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rvtrace-funnel.
+
 config RVTRACE_ENCODER
 	tristate "RISCV Trace Encoder driver"
 	depends on RVTRACE
+	select RVTRACE_FUNNEL
 	help
 	  This driver provides support for the Trace Encoder module, tracing
 	  the instructions that a processor is executing. This is primarily
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 9a526b1fb95a..409a0abf0642 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -59,5 +59,6 @@ coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
 obj-$(CONFIG_RVTRACE) += rvtrace.o
 rvtrace-y := rvtrace-core.o
+obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o
 obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
 rvtrace-encoder-y := rvtrace-encoder-core.o rvtrace-encoder-sysfs.o
diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.c b/drivers/hwtracing/coresight/rvtrace-funnel.c
new file mode 100644
index 000000000000..0dc7799a64ac
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-funnel.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/coresight.h>
+#include <linux/platform_device.h>
+#include <linux/rvtrace.h>
+
+#include "coresight-priv.h"
+#include "rvtrace-funnel.h"
+
+DEFINE_CORESIGHT_DEVLIST(funnel_devs, "rvtrace_funnel");
+
+static int funnel_enable_hw(struct rvtrace_component *comp, int port)
+{
+	u32 disinput;
+	int ret = 0;
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	if (!comp->was_reset) {
+		ret = rvtrace_component_reset(comp);
+		if (ret)
+			goto done;
+	}
+
+	disinput = readl_relaxed(comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET);
+	disinput &= ~(1 << port);
+	writel_relaxed(disinput, comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET);
+	ret = rvtrace_poll_bit(comp, RVTRACE_FUNNEL_DISINPUT_OFFSET, port, 0);
+	if (ret)
+		goto done;
+
+	if (!funnel_data->was_enabled) {
+		ret = rvtrace_enable_component(comp);
+		if (ret)
+			goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int funnel_enable(struct coresight_device *csdev,
+			 struct coresight_connection *in,
+			 struct coresight_connection *out)
+{
+	int ret = 0;
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+	unsigned long flags;
+	bool first_enable = false;
+
+	spin_lock_irqsave(&funnel_data->spinlock, flags);
+	if (in->dest_refcnt == 0) {
+		ret = funnel_enable_hw(comp, in->dest_port);
+		if (!ret)
+			first_enable = true;
+	}
+	if (!ret) {
+		in->dest_refcnt++;
+		funnel_data->input_refcnt++;
+	}
+
+	if (first_enable)
+		dev_dbg(&csdev->dev, "Trace funnel inport %d enabled\n",
+			in->dest_port);
+
+	spin_unlock_irqrestore(&funnel_data->spinlock, flags);
+
+	return ret;
+}
+
+static void funnel_disable_hw(struct rvtrace_component *comp, int inport)
+{
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	if (--funnel_data->input_refcnt != 0)
+		return;
+
+	writel_relaxed(RVTRACE_FUNNEL_DISINPUT_MASK, comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET);
+
+	if (rvtrace_disable_component(comp))
+		dev_err(&funnel_data->csdev->dev,
+			"timeout while waiting for Trace Funnel to be disabled\n");
+
+	if (rvtrace_comp_is_empty(comp))
+		dev_err(&funnel_data->csdev->dev,
+			"timeout while waiting for Trace Funnel internal buffers become empty\n");
+}
+
+static void funnel_disable(struct coresight_device *csdev,
+			   struct coresight_connection *in,
+			   struct coresight_connection *out)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+	unsigned long flags;
+	bool last_disable = false;
+
+	spin_lock_irqsave(&funnel_data->spinlock, flags);
+	if (--in->dest_refcnt == 0) {
+		funnel_disable_hw(comp, in->dest_port);
+		last_disable = true;
+	}
+	spin_unlock_irqrestore(&funnel_data->spinlock, flags);
+
+	if (last_disable)
+		dev_dbg(&csdev->dev, "Trace funnel inport %d disabled\n",
+			in->dest_port);
+
+	if (funnel_data->input_refcnt == 0)
+		dev_dbg(&csdev->dev, "Trace funnel disabled\n");
+}
+
+static const struct coresight_ops_link funnel_link_ops = {
+	.enable		= funnel_enable,
+	.disable	= funnel_disable,
+};
+
+static const struct coresight_ops funnel_cs_ops = {
+	.link_ops       = &funnel_link_ops,
+};
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+	unsigned long flags;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	spin_lock_irqsave(&funnel_data->spinlock, flags);
+
+	if (val) {
+		if (rvtrace_component_reset(comp)) {
+			comp->was_reset = false;
+			spin_unlock_irqrestore(&funnel_data->spinlock, flags);
+			return -EINVAL;
+		}
+	}
+
+	spin_unlock_irqrestore(&funnel_data->spinlock, flags);
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+
+	val = comp->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(cpu);
+
+static struct attribute *trace_funnel_attrs[] = {
+	coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET),
+	coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET),
+	coresight_simple_reg32(disinput, RVTRACE_FUNNEL_DISINPUT_OFFSET),
+	&dev_attr_reset.attr,
+	&dev_attr_cpu.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(trace_funnel);
+
+static int funnel_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata;
+	struct funnel_data *funnel_data;
+	struct rvtrace_component *comp;
+	struct coresight_desc desc = { 0 };
+
+	comp = rvtrace_register_component(pdev);
+	if (IS_ERR(comp))
+		return PTR_ERR(comp);
+
+	funnel_data = devm_kzalloc(dev, sizeof(*funnel_data), GFP_KERNEL);
+	if (!funnel_data)
+		return -ENOMEM;
+
+	spin_lock_init(&funnel_data->spinlock);
+
+	pdata = coresight_get_platform_data(dev);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	pdev->dev.platform_data = pdata;
+
+	platform_set_drvdata(pdev, comp);
+
+	desc.name = coresight_alloc_device_name(&funnel_devs, dev);
+	desc.access = CSDEV_ACCESS_IOMEM(comp->base);
+	desc.type = CORESIGHT_DEV_TYPE_LINK;
+	desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
+	desc.ops = &funnel_cs_ops;
+	desc.pdata = pdata;
+	desc.dev = dev;
+	desc.groups = trace_funnel_groups;
+	funnel_data->csdev = coresight_register(&desc);
+	if (IS_ERR(funnel_data->csdev))
+		return PTR_ERR(funnel_data->csdev);
+
+	comp->id.data = funnel_data;
+
+	dev_dbg(dev, "Trace Funnel initialized\n");
+
+	return 0;
+}
+
+static void funnel_remove(struct platform_device *pdev)
+{
+	struct rvtrace_component *comp = platform_get_drvdata(pdev);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	coresight_unregister(funnel_data->csdev);
+}
+
+static const struct of_device_id funnel_match[] = {
+	{.compatible = "riscv,trace-funnel"},
+	{},
+};
+
+static struct platform_driver funnel_driver = {
+	.probe = funnel_probe,
+	.remove = funnel_remove,
+	.driver = {
+		.name	= "trace-funnel",
+		.of_match_table = funnel_match,
+	},
+};
+
+module_platform_driver(funnel_driver);
+
+MODULE_DESCRIPTION("RISC-V Trace funnel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.h b/drivers/hwtracing/coresight/rvtrace-funnel.h
new file mode 100644
index 000000000000..e5247e6c1bf5
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-funnel.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#ifndef _RVTRACE_FUNNEL_H
+#define _RVTRACE_FUNNEL_H
+
+#include <linux/spinlock.h>
+#include <linux/coresight.h>
+
+/* Disable Individual Funnel Inputs */
+#define RVTRACE_FUNNEL_DISINPUT_OFFSET			0x008
+#define RVTRACE_FUNNEL_DISINPUT_MASK			0xffff
+
+/**
+ * struct funnel_data - specifics associated to a Trace Funnel component
+ * @csdev:        Component vitals needed by the framework.
+ * @spinlock:     Only one at a time pls.
+ * @was_enabled:  Flag showing whether the Trace Funnel was enabled.
+ * @input_refcnt: Record the number of funnel inputs
+ */
+struct funnel_data {
+	struct coresight_device	*csdev;
+	spinlock_t		spinlock;
+	bool			was_enabled;
+	u32			input_refcnt;
+	u32			disintput;
+};
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (3 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Zane Leung
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add initial implementation of RISC-V trace ATB
Bridge driver. The ATB Bridge is defined in the
RISC-V Trace Control Interface specification.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/coresight/Kconfig           |  12 +
 drivers/hwtracing/coresight/Makefile          |   1 +
 .../hwtracing/coresight/rvtrace-atbbridge.c   | 239 ++++++++++++++++++
 3 files changed, 252 insertions(+)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 8381a84e2699..1cd88a1a7d08 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -288,6 +288,17 @@ config RVTRACE
 	  dynamically aggregated with CoreSight trace infrastructure
 	  at run time to form a complete trace path.
 
+config RVTRACE_ATBBRIDGE
+	tristate "RISCV Trace ATB Bridge driver"
+	depends on RVTRACE
+	help
+	  This driver provides support for the ATB Bridge driver. The ATB
+	  Bridge allows sending RISC-V trace to Arm CoreSight infrastructure
+	  (instead of RISC-V compliant sink) as an ATB initiator.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rvtrace-atbbridge.
+
 config RVTRACE_FUNNEL
 	tristate "RISCV Trace Funnel driver"
 	depends on RVTRACE
@@ -303,6 +314,7 @@ config RVTRACE_ENCODER
 	tristate "RISCV Trace Encoder driver"
 	depends on RVTRACE
 	select RVTRACE_FUNNEL
+	select RVTRACE_ATBBRIDGE
 	help
 	  This driver provides support for the Trace Encoder module, tracing
 	  the instructions that a processor is executing. This is primarily
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 409a0abf0642..2264a313a773 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -59,6 +59,7 @@ coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
 obj-$(CONFIG_RVTRACE) += rvtrace.o
 rvtrace-y := rvtrace-core.o
+obj-$(CONFIG_RVTRACE_ATBBRIDGE) += rvtrace-atbbridge.o
 obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o
 obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
 rvtrace-encoder-y := rvtrace-encoder-core.o rvtrace-encoder-sysfs.o
diff --git a/drivers/hwtracing/coresight/rvtrace-atbbridge.c b/drivers/hwtracing/coresight/rvtrace-atbbridge.c
new file mode 100644
index 000000000000..a931be322a2f
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-atbbridge.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/coresight.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/rvtrace.h>
+
+#include "coresight-priv.h"
+#include "coresight-trace-id.h"
+
+#define RVTRACE_ATBBRIDGE_CONTROL_ID_MASK	GENMASK(14, 8)
+/**
+ * struct atbbridge_data - specifics associated to a ATB bridge component
+ * @csdev:      Component vitals needed by the framework.
+ * @spinlock:   Only one at a time pls.
+ * @traceid:    Value of the current ID for this component.
+ */
+struct atbbridge_data {
+	struct coresight_device	*csdev;
+	spinlock_t		spinlock;
+	u8			traceid;
+};
+
+DEFINE_CORESIGHT_DEVLIST(atbbridge_devs, "atbbridge");
+
+static int atbbridge_enable(struct coresight_device *csdev,
+			    struct coresight_connection *in,
+			    struct coresight_connection *out)
+{
+	u32 control;
+	int ret = 0;
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+	unsigned long flags;
+
+	spin_lock_irqsave(&atbbridge_data->spinlock, flags);
+	if (!comp->was_reset) {
+		ret = rvtrace_component_reset(comp);
+		if (ret)
+			goto done;
+	}
+
+	control = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+	control |= FIELD_PREP(RVTRACE_ATBBRIDGE_CONTROL_ID_MASK, atbbridge_data->traceid);
+	writel_relaxed(control, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
+
+	ret = rvtrace_enable_component(comp);
+	if (ret)
+		goto done;
+
+	spin_unlock_irqrestore(&atbbridge_data->spinlock, flags);
+
+	dev_dbg(&csdev->dev, "Trace ATB bridge enabled\n");
+done:
+	return ret;
+}
+
+static void atbbridge_disable(struct coresight_device *csdev,
+			      struct coresight_connection *in,
+			      struct coresight_connection *out)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+	unsigned long flags;
+
+	spin_lock_irqsave(&atbbridge_data->spinlock, flags);
+
+	if (rvtrace_disable_component(comp))
+		dev_err(&csdev->dev,
+			"timeout while waiting for Trace ATB Bridge to be disabled\n");
+
+	if (rvtrace_comp_is_empty(comp))
+		dev_err(&csdev->dev,
+			"timeout while waiting for Trace ATB Bridge internal buffers become empty\n");
+
+	spin_unlock_irqrestore(&atbbridge_data->spinlock, flags);
+
+	dev_dbg(&csdev->dev, "Trace ATB bridge disabled\n");
+}
+
+static int atbbridge_link_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
+				   __maybe_unused struct coresight_device *sink)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(csdev->dev.parent);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+
+	return atbbridge_data->traceid;
+}
+
+static const struct coresight_ops_link atbbridge_link_ops = {
+	.enable		= atbbridge_enable,
+	.disable	= atbbridge_disable,
+};
+
+static const struct coresight_ops atbbridge_cs_ops = {
+	.trace_id	= atbbridge_link_trace_id,
+	.link_ops       = &atbbridge_link_ops,
+};
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+	unsigned long flags;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	if (val) {
+		if (rvtrace_component_reset(comp)) {
+			comp->was_reset = false;
+			spin_unlock_irqrestore(&atbbridge_data->spinlock, flags);
+			return -EINVAL;
+		}
+	}
+
+	spin_unlock_irqrestore(&atbbridge_data->spinlock, flags);
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+
+	val = comp->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(cpu);
+
+static ssize_t traceid_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+
+	val = atbbridge_data->traceid;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(traceid);
+
+static struct attribute *trace_atbbridge_attrs[] = {
+	coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET),
+	coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET),
+	&dev_attr_reset.attr,
+	&dev_attr_cpu.attr,
+	&dev_attr_traceid.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(trace_atbbridge);
+
+static int atbbridge_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata;
+	struct atbbridge_data *atbbridge_data;
+	struct rvtrace_component *comp;
+	struct coresight_desc desc = { 0 };
+	int trace_id = 0;
+
+	comp = rvtrace_register_component(pdev);
+	if (IS_ERR(comp))
+		return PTR_ERR(comp);
+
+	atbbridge_data = devm_kzalloc(dev, sizeof(*atbbridge_data), GFP_KERNEL);
+	if (!atbbridge_data)
+		return -ENOMEM;
+
+	spin_lock_init(&atbbridge_data->spinlock);
+
+	pdata = coresight_get_platform_data(dev);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	pdev->dev.platform_data = pdata;
+
+	platform_set_drvdata(pdev, comp);
+
+	desc.name = coresight_alloc_device_name(&atbbridge_devs, dev);
+	desc.access = CSDEV_ACCESS_IOMEM(comp->base);
+	desc.type = CORESIGHT_DEV_TYPE_LINK;
+	desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
+	desc.ops = &atbbridge_cs_ops;
+	desc.pdata = pdata;
+	desc.dev = dev;
+	desc.groups = trace_atbbridge_groups;
+	atbbridge_data->csdev = coresight_register(&desc);
+	if (IS_ERR(atbbridge_data->csdev))
+		return PTR_ERR(atbbridge_data->csdev);
+
+	trace_id = coresight_trace_id_get_system_id();
+	if (trace_id < 0)
+		return -EINVAL;
+	atbbridge_data->traceid = (u8)trace_id;
+
+	comp->id.data = atbbridge_data;
+
+	dev_dbg(dev, "Trace ATB Bridge initialized\n");
+
+	return 0;
+}
+
+static void atbbridge_remove(struct platform_device *pdev)
+{
+	struct rvtrace_component *comp = platform_get_drvdata(pdev);
+	struct atbbridge_data *atbbridge_data = rvtrace_component_data(comp);
+
+	coresight_trace_id_put_system_id(atbbridge_data->traceid);
+	coresight_unregister(atbbridge_data->csdev);
+}
+
+static const struct of_device_id atbbridge_match[] = {
+	{.compatible = "riscv,trace-atbbridge"},
+	{},
+};
+
+static struct platform_driver atbbridge_driver = {
+	.probe = atbbridge_probe,
+	.remove = atbbridge_remove,
+	.driver = {
+		.name	= "trace-atbbridge",
+		.of_match_table = atbbridge_match,
+	},
+};
+
+module_platform_driver(atbbridge_driver);
+
+MODULE_DESCRIPTION("RISC-V Trace ATB Bridge driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (4 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Zane Leung
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Implement timestamp as a configurable sub-component for both encoder
and funnel drivers.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 drivers/hwtracing/coresight/Makefile          |   2 +-
 .../coresight/rvtrace-encoder-core.c          |  42 ++-
 .../coresight/rvtrace-encoder-sysfs.c         |  50 ++++
 drivers/hwtracing/coresight/rvtrace-encoder.h |   8 +
 drivers/hwtracing/coresight/rvtrace-funnel.c  |  95 +++++-
 drivers/hwtracing/coresight/rvtrace-funnel.h  |   8 +
 .../hwtracing/coresight/rvtrace-timestamp.c   | 278 ++++++++++++++++++
 .../hwtracing/coresight/rvtrace-timestamp.h   |  64 ++++
 8 files changed, 543 insertions(+), 4 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
 create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 2264a313a773..3ad1b0399f86 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -58,7 +58,7 @@ obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
 coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
 obj-$(CONFIG_RVTRACE) += rvtrace.o
-rvtrace-y := rvtrace-core.o
+rvtrace-y := rvtrace-core.o rvtrace-timestamp.o
 obj-$(CONFIG_RVTRACE_ATBBRIDGE) += rvtrace-atbbridge.o
 obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o
 obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-core.c b/drivers/hwtracing/coresight/rvtrace-encoder-core.c
index 149a3cd97602..7bd075b8ce4f 100644
--- a/drivers/hwtracing/coresight/rvtrace-encoder-core.c
+++ b/drivers/hwtracing/coresight/rvtrace-encoder-core.c
@@ -14,9 +14,11 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/rvtrace.h>
 
 #include "rvtrace-encoder.h"
+#include "rvtrace-timestamp.h"
 #include "coresight-etm-perf.h"
 
 static int boot_enable;
@@ -172,6 +174,10 @@ static void encoder_set_config(struct rvtrace_component *comp)
 			 config->inst_syncmax);
 		config->inst_syncmax = BMVAL(val, 20, 23);
 	}
+
+	/* Configure timestamp only if encoder has timestamp component */
+	if (encoder_data->has_timestamp && encoder_data->ts_ctrl)
+		timestamp_set_config(comp, dev, &encoder_data->ts_config);
 }
 
 static int encoder_enable_hw(struct rvtrace_component *comp)
@@ -192,6 +198,16 @@ static int encoder_enable_hw(struct rvtrace_component *comp)
 	if (ret)
 		goto done;
 
+	/* Enable timestamp only if encoder has timestamp component */
+	if (encoder_data->has_timestamp && encoder_data->ts_ctrl) {
+		ret = timestamp_enable(comp);
+		if (ret) {
+			dev_warn(&encoder_data->csdev->dev,
+				 "Failed to enable timestamp\n");
+			ret = 0;  /* Don't fail encoder enable if timestamp fails */
+		}
+	}
+
 	val = readl_relaxed(comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
 	val |= RVTRACE_ENCODER_ITRACE;
 	writel_relaxed(val, comp->base + RVTRACE_COMPONENT_CTRL_OFFSET);
@@ -283,6 +299,10 @@ static void encoder_disable_hw(struct rvtrace_component *comp)
 {
 	struct encoder_data *encoder_data = rvtrace_component_data(comp);
 
+	/* Disable timestamp only if encoder has timestamp component */
+	if (encoder_data->has_timestamp && encoder_data->ts_ctrl)
+		timestamp_disable(comp);
+
 	if (rvtrace_disable_component(comp))
 		dev_err(&encoder_data->csdev->dev,
 			"timeout while waiting for Trace Encoder become disabled\n");
@@ -431,6 +451,26 @@ static int encoder_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, comp);
 
+	/* Check if encoder has timestamp component from device tree early */
+	encoder_data->has_timestamp = fwnode_property_present(dev->fwnode,
+							      "riscv,timestamp-present");
+	if (encoder_data->has_timestamp) {
+		if (rvtrace_init_timestamp(comp, &encoder_data->ts_config)) {
+			dev_err(dev, "Timestamp initialization failed\n");
+			return -EINVAL;
+		}
+
+		/* TODO: Default to enabling timestamp control if present, as
+		 * encoder_data->ts_ctrl can be configured via sysfs attribute,
+		 * but not through perf_event at this time. Future versions may
+		 * add support for configuring timestamps via perf_event.
+		 */
+		encoder_data->ts_ctrl = true;
+	}
+
+	/* Set component data before registration so is_visible callbacks can access it */
+	comp->id.data = encoder_data;
+
 	desc.name = devm_kasprintf(dev, GFP_KERNEL, "encoder%d", comp->cpu);
 	if (!desc.name)
 		return -ENOMEM;
@@ -452,8 +492,6 @@ static int encoder_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	comp->id.data = encoder_data;
-
 	rvtrace_cpu_encoder[comp->cpu] = comp;
 
 	encoder_set_default(comp);
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
index c89af4cb591a..520f5a16f9a0 100644
--- a/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
+++ b/drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
@@ -7,6 +7,7 @@
 #include <linux/sysfs.h>
 #include <linux/rvtrace.h>
 #include "rvtrace-encoder.h"
+#include "rvtrace-timestamp.h"
 #include "coresight-priv.h"
 
 static ssize_t cpu_show(struct device *dev,
@@ -48,9 +49,38 @@ static ssize_t reset_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(reset);
 
+static ssize_t ts_ctrl_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", encoder_data->ts_ctrl);
+}
+
+static ssize_t ts_ctrl_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	spin_lock(&encoder_data->spinlock);
+	encoder_data->ts_ctrl = !!val;
+	spin_unlock(&encoder_data->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ts_ctrl);
+
 static struct attribute *trace_encoder_attrs[] = {
 	&dev_attr_cpu.attr,
 	&dev_attr_reset.attr,
+	&dev_attr_ts_ctrl.attr,
 	NULL,
 };
 
@@ -285,6 +315,19 @@ static struct attribute *trace_encoder_features_attrs[] = {
 	NULL,
 };
 
+static umode_t timestamp_attr_is_visible(struct kobject *kobj,
+					 struct attribute *attr, int idx)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+	if (encoder_data->has_timestamp)
+		return attr->mode;
+
+	return 0;
+}
+
 static const struct attribute_group trace_encoder_group = {
 	.attrs = trace_encoder_attrs,
 };
@@ -304,10 +347,17 @@ static const struct attribute_group trace_encoder_features_group = {
 	.name = "features",
 };
 
+static const struct attribute_group trace_encoder_timestamp_group = {
+	.attrs = (struct attribute **)timestamp_attrs,
+	.name = "timestamp",
+	.is_visible = timestamp_attr_is_visible,
+};
+
 const struct attribute_group *trace_encoder_groups[] = {
 	&trace_encoder_group,
 	&trace_encoder_mgmt_group,
 	&trace_encoder_control_group,
 	&trace_encoder_features_group,
+	&trace_encoder_timestamp_group,
 	NULL,
 };
diff --git a/drivers/hwtracing/coresight/rvtrace-encoder.h b/drivers/hwtracing/coresight/rvtrace-encoder.h
index 44f2066ad869..7a0ada11de8d 100644
--- a/drivers/hwtracing/coresight/rvtrace-encoder.h
+++ b/drivers/hwtracing/coresight/rvtrace-encoder.h
@@ -9,6 +9,7 @@
 #include <asm/local.h>
 #include <linux/spinlock.h>
 #include "coresight-priv.h"
+#include "rvtrace-timestamp.h"
 
 /* Trace Encoder Control Register */
 #define RVTRACE_ENCODER_ITRACE				    BIT(2)
@@ -126,7 +127,10 @@ struct encoder_config {
  * @spinlock:	    Only one at a time pls.
  * @sticky_enable:  True if trace encoder base configuration has been done.
  * @boot_enable:    True if we should start tracing at boot time.
+ * @has_timestamp:  True if this encoder has timestamp component.
+ * @ts_ctrl:        Controls the insertion of global timestamps in the trace streams.
  * @config:	    Structure holding configuration parameters.
+ * @ts_config:	    Timestamp configuration.
  */
 
 struct encoder_data {
@@ -134,10 +138,14 @@ struct encoder_data {
 	spinlock_t			spinlock;
 	bool				sticky_enable;
 	bool				boot_enable;
+	bool				has_timestamp;
+	bool				ts_ctrl;
 	struct encoder_config		config;
+	struct timestamp_config		ts_config;
 };
 
 extern const struct attribute_group *trace_encoder_groups[];
+extern const struct attribute *timestamp_attrs[];
 void encoder_set_default(struct rvtrace_component *comp);
 
 #endif
diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.c b/drivers/hwtracing/coresight/rvtrace-funnel.c
index 0dc7799a64ac..ab7cdb29e5fd 100644
--- a/drivers/hwtracing/coresight/rvtrace-funnel.c
+++ b/drivers/hwtracing/coresight/rvtrace-funnel.c
@@ -6,9 +6,11 @@
 #include <linux/kernel.h>
 #include <linux/coresight.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/rvtrace.h>
 
 #include "coresight-priv.h"
+#include "rvtrace-timestamp.h"
 #include "rvtrace-funnel.h"
 
 DEFINE_CORESIGHT_DEVLIST(funnel_devs, "rvtrace_funnel");
@@ -36,6 +38,16 @@ static int funnel_enable_hw(struct rvtrace_component *comp, int port)
 		ret = rvtrace_enable_component(comp);
 		if (ret)
 			goto done;
+
+		/* Enable timestamp only if funnel has timestamp component */
+		if (funnel_data->has_timestamp && funnel_data->ts_ctrl) {
+			ret = timestamp_enable(comp);
+			if (ret) {
+				dev_warn(&funnel_data->csdev->dev,
+					 "Failed to enable timestamp\n");
+				ret = 0;  /* Don't fail funnel enable if timestamp fails */
+			}
+		}
 	}
 
 done:
@@ -79,6 +91,10 @@ static void funnel_disable_hw(struct rvtrace_component *comp, int inport)
 	if (--funnel_data->input_refcnt != 0)
 		return;
 
+	/* Disable timestamp only if funnel has timestamp component */
+	if (funnel_data->has_timestamp && funnel_data->ts_ctrl)
+		timestamp_disable(comp);
+
 	writel_relaxed(RVTRACE_FUNNEL_DISINPUT_MASK, comp->base + RVTRACE_FUNNEL_DISINPUT_OFFSET);
 
 	if (rvtrace_disable_component(comp))
@@ -162,15 +178,72 @@ static ssize_t cpu_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(cpu);
 
+static ssize_t ts_ctrl_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", funnel_data->ts_ctrl);
+}
+
+static ssize_t ts_ctrl_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	spin_lock(&funnel_data->spinlock);
+	funnel_data->ts_ctrl = !!val;
+	spin_unlock(&funnel_data->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ts_ctrl);
+
 static struct attribute *trace_funnel_attrs[] = {
 	coresight_simple_reg32(control, RVTRACE_COMPONENT_CTRL_OFFSET),
 	coresight_simple_reg32(impl, RVTRACE_COMPONENT_IMPL_OFFSET),
 	coresight_simple_reg32(disinput, RVTRACE_FUNNEL_DISINPUT_OFFSET),
 	&dev_attr_reset.attr,
 	&dev_attr_cpu.attr,
+	&dev_attr_ts_ctrl.attr,
+	NULL,
+};
+
+static umode_t timestamp_attr_is_visible(struct kobject *kobj,
+					 struct attribute *attr, int idx)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+	if (funnel_data->has_timestamp)
+		return attr->mode;
+
+	return 0;
+}
+
+static struct attribute_group trace_funnel_group = {
+	.attrs = trace_funnel_attrs,
+};
+
+static struct attribute_group trace_funnel_timestamp_group = {
+	.attrs = (struct attribute **)timestamp_attrs,
+	.name = "timestamp",
+	.is_visible = timestamp_attr_is_visible,
+};
+
+const struct attribute_group *trace_funnel_groups[] = {
+	&trace_funnel_group,
+	&trace_funnel_timestamp_group,
 	NULL,
 };
-ATTRIBUTE_GROUPS(trace_funnel);
 
 static int funnel_probe(struct platform_device *pdev)
 {
@@ -197,6 +270,26 @@ static int funnel_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, comp);
 
+	/* Check if funnel has timestamp component from device tree */
+	funnel_data->has_timestamp = fwnode_property_present(dev->fwnode,
+							     "riscv,timestamp-present");
+	if (funnel_data->has_timestamp) {
+		if (rvtrace_init_timestamp(comp, &funnel_data->ts_config)) {
+			dev_err(dev, "Timestamp initialization failed\n");
+			return -EINVAL;
+		}
+
+		/* TODO: Default to enabling timestamp control if present, as
+		 * encoder_data->ts_ctrl can be configured via sysfs attribute,
+		 * but not through perf_event at this time. Future versions may
+		 * add support for configuring timestamps via perf_event.
+		 */
+		funnel_data->ts_ctrl = true;
+	}
+
+	/* Set component data before registration so is_visible callbacks can access it */
+	comp->id.data = funnel_data;
+
 	desc.name = coresight_alloc_device_name(&funnel_devs, dev);
 	desc.access = CSDEV_ACCESS_IOMEM(comp->base);
 	desc.type = CORESIGHT_DEV_TYPE_LINK;
diff --git a/drivers/hwtracing/coresight/rvtrace-funnel.h b/drivers/hwtracing/coresight/rvtrace-funnel.h
index e5247e6c1bf5..34394f775faa 100644
--- a/drivers/hwtracing/coresight/rvtrace-funnel.h
+++ b/drivers/hwtracing/coresight/rvtrace-funnel.h
@@ -19,6 +19,9 @@
  * @spinlock:     Only one at a time pls.
  * @was_enabled:  Flag showing whether the Trace Funnel was enabled.
  * @input_refcnt: Record the number of funnel inputs
+ * @has_timestamp: True if this funnel has timestamp component.
+ * @ts_ctrl:      Controls the insertion of global timestamps in the trace streams.
+ * @ts_config:    Timestamp configuration.
  */
 struct funnel_data {
 	struct coresight_device	*csdev;
@@ -26,6 +29,11 @@ struct funnel_data {
 	bool			was_enabled;
 	u32			input_refcnt;
 	u32			disintput;
+	bool			has_timestamp;
+	bool			ts_ctrl;
+	struct timestamp_config	ts_config;
 };
 
+extern const struct attribute *timestamp_attrs[];
+
 #endif
diff --git a/drivers/hwtracing/coresight/rvtrace-timestamp.c b/drivers/hwtracing/coresight/rvtrace-timestamp.c
new file mode 100644
index 000000000000..a82714f43d7c
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-timestamp.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/bitfield.h>
+#include <linux/sysfs.h>
+#include <linux/rvtrace.h>
+
+#include "rvtrace-timestamp.h"
+#include "rvtrace-encoder.h"
+#include "rvtrace-funnel.h"
+#include "coresight-priv.h"
+
+int timestamp_enable(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+	val |= (RVTRACE_TIMESTAMP_ENABLE | RVTRACE_TIMESTAMP_COUNT);
+
+	writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+
+	return rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET,
+				RVTRACE_TIMESTAMP_ENABLE_SHIFT, 1);
+}
+EXPORT_SYMBOL_GPL(timestamp_enable);
+
+void timestamp_disable(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+	val &= ~(RVTRACE_TIMESTAMP_ENABLE | RVTRACE_TIMESTAMP_COUNT);
+	writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+}
+EXPORT_SYMBOL_GPL(timestamp_disable);
+
+struct timestamp_config *timestamp_get_config(struct rvtrace_component *comp)
+{
+	if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+		struct encoder_data *encoder_data = rvtrace_component_data(comp);
+
+		return &encoder_data->ts_config;
+	} else if (comp->id.type == RVTRACE_COMPONENT_TYPE_FUNNEL) {
+		struct funnel_data *funnel_data = rvtrace_component_data(comp);
+
+		return &funnel_data->ts_config;
+	} else {
+		return NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(timestamp_get_config);
+
+void timestamp_set_config(struct rvtrace_component *comp, struct device *dev,
+			  struct timestamp_config *config)
+{
+	u32 val;
+
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+
+	val |= (FIELD_PREP(RVTRACE_TIMESTAMP_RUN_IN_DEBUG, config->run_in_debug) |
+		FIELD_PREP(RVTRACE_TIMESTAMP_MODE, config->mode) |
+		FIELD_PREP(RVTRACE_TIMESTAMP_PRESCALE, config->prescale));
+
+	writel_relaxed(val, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+
+	/* Verify configuration was applied */
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+
+	if (BMVAL(val, 3, 3) != config->run_in_debug) {
+		dev_warn(dev, "timestamp run_in_debug %#x not supported\n",
+			 config->run_in_debug);
+		config->run_in_debug = BMVAL(val, 3, 3);
+	}
+
+	if (BMVAL(val, 4, 6) != config->mode) {
+		dev_warn(dev, "timestamp mode %#x not supported\n",
+			 config->mode);
+		config->mode = BMVAL(val, 4, 6);
+	}
+
+	if (BMVAL(val, 8, 9) != config->prescale) {
+		dev_warn(dev, "timestamp prescale %#x not supported\n",
+			 config->prescale);
+		config->prescale = BMVAL(val, 8, 9);
+	}
+}
+EXPORT_SYMBOL_GPL(timestamp_set_config);
+
+static int rvtrace_timestamp_reset(struct rvtrace_component *comp)
+{
+	int ret;
+
+	writel_relaxed(0, comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+	ret = rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET,
+			       RVTRACE_TIMESTAMP_ACTIVE_SHIFT, 0);
+
+	if (ret)
+		return ret;
+
+	writel_relaxed(RVTRACE_TIMESTAMP_ACTIVE,
+			comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp, RVTRACE_TIMESTAMP_CTRL_OFFSET,
+				RVTRACE_TIMESTAMP_ACTIVE_SHIFT, 1);
+}
+
+int rvtrace_init_timestamp(struct rvtrace_component *comp,
+			   struct timestamp_config *config)
+{
+	u32 val;
+	int ret;
+
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+	if (!FIELD_GET(RVTRACE_TIMESTAMP_ACTIVE, val)) {
+		ret = rvtrace_timestamp_reset(comp);
+		if (ret)
+			return ret;
+	}
+
+	val = readl_relaxed(comp->base + RVTRACE_TIMESTAMP_CTRL_OFFSET);
+
+	config->run_in_debug = !!(val & RVTRACE_TIMESTAMP_RUN_IN_DEBUG);
+	config->mode = BMVAL(val, 4, 6);
+	config->prescale = BMVAL(val, 8, 9);
+	config->width = BMVAL(val, 24, 29);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_init_timestamp);
+
+static ssize_t run_in_debug_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", config->run_in_debug);
+}
+
+static ssize_t run_in_debug_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+	unsigned long val;
+
+	if (!config)
+		return -EINVAL;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+
+	config->run_in_debug = !!val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(run_in_debug);
+
+static const char * const modes_str[] = {
+	[TIMESTAMP_MODE_NONE] = "none",
+	[TIMESTAMP_MODE_EXTERNAL] = "external",
+	[TIMESTAMP_MODE_INTERNAL_SYSTEM] = "internal_system",
+	[TIMESTAMP_MODE_INTERNAL_CORE] = "internal_core",
+	[TIMESTAMP_MODE_SHARED] = "shared",
+};
+
+static ssize_t modes_available_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%s %s %s %s %s\n",
+			    modes_str[TIMESTAMP_MODE_NONE],
+			    modes_str[TIMESTAMP_MODE_EXTERNAL],
+			    modes_str[TIMESTAMP_MODE_INTERNAL_SYSTEM],
+			    modes_str[TIMESTAMP_MODE_INTERNAL_CORE],
+			    modes_str[TIMESTAMP_MODE_SHARED]);
+}
+static DEVICE_ATTR_RO(modes_available);
+
+static ssize_t mode_preferred_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", modes_str[config->mode]);
+}
+
+static ssize_t mode_preferred_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_NONE]))
+		config->mode = TIMESTAMP_MODE_NONE;
+	else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_EXTERNAL]))
+		config->mode = TIMESTAMP_MODE_EXTERNAL;
+	else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_INTERNAL_SYSTEM]))
+		config->mode = TIMESTAMP_MODE_INTERNAL_SYSTEM;
+	else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_INTERNAL_CORE]))
+		config->mode = TIMESTAMP_MODE_INTERNAL_CORE;
+	else if (sysfs_streq(buf, modes_str[TIMESTAMP_MODE_SHARED]))
+		config->mode = TIMESTAMP_MODE_SHARED;
+	else
+		return -EINVAL;
+	return size;
+}
+static DEVICE_ATTR_RW(mode_preferred);
+
+static ssize_t prescale_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", config->prescale);
+}
+
+static ssize_t prescale_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	unsigned long val;
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	config->prescale = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(prescale);
+
+static ssize_t width_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct rvtrace_component *comp = dev_get_drvdata(dev->parent);
+	struct timestamp_config *config = timestamp_get_config(comp);
+
+	if (!config)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", config->width);
+}
+static DEVICE_ATTR_RO(width);
+
+const struct attribute *timestamp_attrs[] = {
+	&dev_attr_run_in_debug.attr,
+	&dev_attr_modes_available.attr,
+	&dev_attr_mode_preferred.attr,
+	&dev_attr_prescale.attr,
+	&dev_attr_width.attr,
+	NULL,
+};
+EXPORT_SYMBOL_GPL(timestamp_attrs);
diff --git a/drivers/hwtracing/coresight/rvtrace-timestamp.h b/drivers/hwtracing/coresight/rvtrace-timestamp.h
new file mode 100644
index 000000000000..13cecddbd82c
--- /dev/null
+++ b/drivers/hwtracing/coresight/rvtrace-timestamp.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ * Author: liangzhen <zhen.liang@spacemit.com>
+ */
+
+#ifndef _RVTRACE_TIMESTAMP_H
+#define _RVTRACE_TIMESTAMP_H
+
+#include <linux/types.h>
+
+/* Timestamp Control Register */
+#define RVTRACE_TIMESTAMP_CTRL_OFFSET			0x040
+#define RVTRACE_TIMESTAMP_ACTIVE			BIT(0)
+#define RVTRACE_TIMESTAMP_COUNT				BIT(1)
+#define RVTRACE_TIMESTAMP_RESET				BIT(2)
+#define RVTRACE_TIMESTAMP_RUN_IN_DEBUG			BIT(3)
+#define RVTRACE_TIMESTAMP_MODE				GENMASK(6, 4)
+#define RVTRACE_TIMESTAMP_PRESCALE			GENMASK(9, 8)
+#define RVTRACE_TIMESTAMP_ENABLE			BIT(15)
+#define RVTRACE_TIMESTAMP_WIDTH				GENMASK(29, 24)
+
+#define RVTRACE_TIMESTAMP_ACTIVE_SHIFT			0
+#define RVTRACE_TIMESTAMP_ENABLE_SHIFT			15
+
+/* Timestamp Counter Lower Bits */
+#define RVTRACE_TIMESTAMP_COUNTER_LOW			0x048
+/* Timestamp Counter Upper Bits */
+#define RVTRACE_TIMESTAMP_COUNTER_HIGH			0x04C
+
+enum timestamp_mode {
+	TIMESTAMP_MODE_NONE = 0,
+	TIMESTAMP_MODE_EXTERNAL = 1,
+	TIMESTAMP_MODE_INTERNAL_SYSTEM = 2,
+	TIMESTAMP_MODE_INTERNAL_CORE = 3,
+	TIMESTAMP_MODE_SHARED = 4
+};
+
+/**
+ * struct timestamp_config - timestamp configuration for encoder/funnel
+ * @run_in_debug:       Continue timestamp counting in debug mode
+ * @mode:		Timestamp generation mode (periodic, event-triggered)
+ * @prescale:		Clock prescale factor (1, 4, 16, 64)
+ * @width:		Timestamp counter width in bits (0-63)
+ */
+struct timestamp_config {
+	bool				run_in_debug;
+	u8				mode;
+	u8				prescale;
+	u8				width;
+};
+
+struct rvtrace_component;
+
+/* Timestamp control functions */
+int timestamp_enable(struct rvtrace_component *comp);
+void timestamp_disable(struct rvtrace_component *comp);
+struct timestamp_config *timestamp_get_config(struct rvtrace_component *comp);
+void timestamp_set_config(struct rvtrace_component *comp, struct device *dev,
+			  struct timestamp_config *config);
+int rvtrace_init_timestamp(struct rvtrace_component *comp,
+			   struct timestamp_config *config);
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 07/12] coresight: Add RISC-V PMU name support
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (5 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Zane Leung
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Define CORESIGHT_ETM_PMU_NAME based on architecture:
- Set to "rvtrace" when CONFIG_RVTRACE is enabled
- Default to "cs_etm" for ARM/ARM64 systems

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 include/linux/coresight-pmu.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
index 2e179abe472a..119565500ec5 100644
--- a/include/linux/coresight-pmu.h
+++ b/include/linux/coresight-pmu.h
@@ -9,7 +9,11 @@
 
 #include <linux/bits.h>
 
+#if IS_ENABLED(CONFIG_RVTRACE)
+#define CORESIGHT_ETM_PMU_NAME "rvtrace"
+#else
 #define CORESIGHT_ETM_PMU_NAME "cs_etm"
+#endif
 
 /*
  * The legacy Trace ID system based on fixed calculation from the cpu
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (6 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

This commit enhances the RISC-V performance tools by allowing the
rvtrace PMU to be listed and selected when auxiliary trace support
is enabled.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/perf/arch/riscv/util/Build |  2 ++
 tools/perf/arch/riscv/util/pmu.c | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+)
 create mode 100644 tools/perf/arch/riscv/util/pmu.c

diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
index 2328fb9a30a3..748068a3a5c5 100644
--- a/tools/perf/arch/riscv/util/Build
+++ b/tools/perf/arch/riscv/util/Build
@@ -1 +1,3 @@
 perf-util-y += header.o
+
+perf-util-y += pmu.o
diff --git a/tools/perf/arch/riscv/util/pmu.c b/tools/perf/arch/riscv/util/pmu.c
new file mode 100644
index 000000000000..71ad1c8884d0
--- /dev/null
+++ b/tools/perf/arch/riscv/util/pmu.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <string.h>
+#include <linux/perf_event.h>
+
+#include "../../../util/pmu.h"
+
+#define RVTRACE_PMU_NAME "rvtrace"
+
+void perf_pmu__arch_init(struct perf_pmu *pmu)
+{
+	if (!strcmp(pmu->name, RVTRACE_PMU_NAME)) {
+		pmu->auxtrace = true;
+		pmu->selectable = true;
+	}
+}
+
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (7 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14 23:31   ` Bo Gan
  2026-04-14  3:41 ` [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Introduce the required auxiliary API functions allowing
the perf core to interact with RISC-V trace perf driver.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/perf/arch/riscv/util/Build      |   2 +-
 tools/perf/arch/riscv/util/auxtrace.c | 489 ++++++++++++++++++++++++++
 tools/perf/util/auxtrace.c            |   1 +
 tools/perf/util/auxtrace.h            |   1 +
 tools/perf/util/rvtrace.h             |  38 ++
 5 files changed, 530 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
 create mode 100644 tools/perf/util/rvtrace.h

diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
index 748068a3a5c5..10f41f97bc2e 100644
--- a/tools/perf/arch/riscv/util/Build
+++ b/tools/perf/arch/riscv/util/Build
@@ -1,3 +1,3 @@
 perf-util-y += header.o
 
-perf-util-y += pmu.o
+perf-util-y += pmu.o auxtrace.o
diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
new file mode 100644
index 000000000000..5feee198ef97
--- /dev/null
+++ b/tools/perf/arch/riscv/util/auxtrace.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+#include <linux/zalloc.h>
+#include <linux/string.h>
+#include <time.h>
+
+#include <internal/lib.h> // page_size
+#include "../../../util/auxtrace.h"
+#include "../../../util/cpumap.h"
+#include "../../../util/debug.h"
+#include "../../../util/event.h"
+#include "../../../util/evlist.h"
+#include "../../../util/evsel.h"
+#include "../../../util/rvtrace.h"
+#include "../../../util/pmu.h"
+#include "../../../util/record.h"
+#include "../../../util/session.h"
+#include "../../../util/tsc.h"
+#include "../../../util/evsel_config.h"
+
+#define RVTRACE_PMU_NAME "rvtrace"
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+
+static const char * const metadata_encoder_ro[] = {
+	[RVTRACE_ENCODER_FORMAT]    = "control/format",
+	[RVTRACE_ENCODER_CONTEXT]   = "control/context",
+	[RVTRACE_ENCODER_INHB_SRC]  = "control/inhb_src",
+	[RVTRACE_ENCODER_SRCBITS]   = "features/srcb",
+	[RVTRACE_ENCODER_SRCID]	    = "features/srcid"
+};
+
+struct rvtrace_recording {
+	struct auxtrace_record	itr;
+	struct perf_pmu *rvtrace_pmu;
+	struct evlist *evlist;
+	bool snapshot_mode;
+	size_t snapshot_size;
+};
+
+static int rvtrace_parse_snapshot_options(struct auxtrace_record *itr,
+					  struct record_opts *opts,
+					  const char *str)
+{
+	struct rvtrace_recording *ptr =
+				container_of(itr, struct rvtrace_recording, itr);
+	unsigned long long snapshot_size = 0;
+	char *endptr;
+
+	if (str) {
+		snapshot_size = strtoull(str, &endptr, 0);
+		if (*endptr || snapshot_size > SIZE_MAX)
+			return -1;
+	}
+
+	opts->auxtrace_snapshot_mode = true;
+	opts->auxtrace_snapshot_size = snapshot_size;
+	ptr->snapshot_size = snapshot_size;
+
+	return 0;
+}
+
+static size_t rvtrace_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+				     struct evlist *evlist __maybe_unused)
+{
+	int encoder;
+	struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
+	struct perf_cpu_map *intersect_cpus;
+
+	if (!perf_cpu_map__has_any_cpu(event_cpus)) {
+		/* cpu map is not "any" CPU , we have specific CPUs to work with */
+		struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+
+		intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
+		perf_cpu_map__put(online_cpus);
+	} else {
+		/* Event can be "any" CPU so count all online CPUs. */
+		intersect_cpus = perf_cpu_map__new_online_cpus();
+	}
+
+	encoder = perf_cpu_map__nr(intersect_cpus);
+	perf_cpu_map__put(intersect_cpus);
+
+	return (RVTRACE_HEADER_SIZE + encoder * RVTRACE_ENCODER_PRIV_SIZE);
+}
+
+static int rvtrace_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val)
+{
+	char pmu_path[PATH_MAX];
+	int scan;
+
+	/* Get RO metadata from sysfs */
+	snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
+
+	scan = perf_pmu__scan_file(pmu, pmu_path, "%llx", val);
+	if (scan != 1) {
+		pr_err("%s: error reading: %s\n", __func__, pmu_path);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rvtrace_get_metadata(struct perf_cpu cpu, u32 *offset,
+				 struct auxtrace_record *itr,
+				 struct perf_record_auxtrace_info *info)
+{
+	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
+	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+
+	info->priv[*offset + RVTRACE_ENCODER_CPU] = cpu.cpu;
+	info->priv[*offset + RVTRACE_ENCODER_NR_TRC_PARAMS] = RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH;
+
+	/* Get read-only information from sysFS */
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_FORMAT],
+			&info->priv[*offset + RVTRACE_ENCODER_FORMAT]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT],
+			&info->priv[*offset + RVTRACE_ENCODER_CONTEXT]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_INHB_SRC],
+			&info->priv[*offset + RVTRACE_ENCODER_INHB_SRC]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCBITS],
+			&info->priv[*offset + RVTRACE_ENCODER_SRCBITS]);
+	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCID],
+			&info->priv[*offset + RVTRACE_ENCODER_SRCID]);
+
+	/* Where the next CPU entry should start from */
+	*offset += RVTRACE_ENCODER_PRIV_MAX;
+}
+
+static int rvtrace_info_fill(struct auxtrace_record *itr, struct perf_session *session,
+			     struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size)
+{
+	int i;
+	u32 offset;
+	u64 nr_cpu, type;
+	struct perf_cpu_map *cpu_map;
+	struct perf_cpu_map *event_cpus = session->evlist->core.user_requested_cpus;
+	struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
+	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+	struct perf_cpu cpu;
+
+	if (priv_size != rvtrace_info_priv_size(itr, session->evlist))
+		return -EINVAL;
+
+	if (!session->evlist->core.nr_mmaps)
+		return -EINVAL;
+
+	/* If the cpu_map has the "any" CPU all online CPUs are involved */
+	if (perf_cpu_map__has_any_cpu(event_cpus)) {
+		cpu_map = online_cpus;
+	} else {
+		/* Make sure all specified CPUs are online */
+		perf_cpu_map__for_each_cpu(cpu, i, event_cpus) {
+			if (!perf_cpu_map__has(online_cpus, cpu))
+				return -EINVAL;
+		}
+
+		cpu_map = event_cpus;
+	}
+
+	nr_cpu = perf_cpu_map__nr(cpu_map);
+	type = rvtrace_pmu->type;
+
+	/* First fill out the session header */
+	auxtrace_info->type = PERF_AUXTRACE_RISCV_TRACE;
+	auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] = type << 32;
+	auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] |= nr_cpu;
+
+	offset = RVTRACE_HEADER_MAX;
+
+	perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
+		assert(offset < priv_size);
+		rvtrace_get_metadata(cpu, &offset, itr, auxtrace_info);
+	}
+
+	perf_cpu_map__put(online_cpus);
+
+	return 0;
+}
+
+static int rvtrace_set_sink_attr(struct perf_pmu *pmu,
+				 struct evsel *evsel)
+{
+	char msg[BUFSIZ], path[PATH_MAX], *sink;
+	struct evsel_config_term *term;
+	int ret = -EINVAL;
+	u32 hash;
+
+	if (evsel->core.attr.config2 & GENMASK(31, 0))
+		return 0;
+
+	list_for_each_entry(term, &evsel->config_terms, list) {
+		if (term->type != EVSEL__CONFIG_TERM_DRV_CFG)
+			continue;
+
+		sink = term->val.str;
+		snprintf(path, PATH_MAX, "sinks/%s", sink);
+
+		ret = perf_pmu__scan_file(pmu, path, "%x", &hash);
+		if (ret != 1) {
+			if (errno == ENOENT)
+				pr_err("Couldn't find sink \"%s\" on event %s\n"
+				       "Missing kernel or device support?\n\n"
+				       "Hint: An appropriate sink will be picked automatically if one isn't specified.\n",
+				       sink, evsel__name(evsel));
+			else
+				pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n",
+				       sink, evsel__name(evsel), errno,
+				       str_error_r(errno, msg, sizeof(msg)));
+			return ret;
+		}
+
+		evsel->core.attr.config2 |= hash;
+		return 0;
+	}
+
+	/*
+	 * No sink was provided on the command line - allow the CoreSight
+	 * system to look for a default
+	 */
+	return 0;
+}
+
+static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist *evlist,
+				     struct record_opts *opts)
+{
+	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
+	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+	struct evsel *evsel, *rvtrace_evsel = NULL;
+	struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
+	bool privileged = perf_event_paranoid_check(-1);
+	struct evsel *tracking_evsel;
+	int err;
+
+	ptr->evlist = evlist;
+	ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
+	evlist__for_each_entry(evlist, evsel) {
+		if (evsel->core.attr.type == rvtrace_pmu->type) {
+			if (rvtrace_evsel) {
+				pr_err("There may be only one " RVTRACE_PMU_NAME "x event\n");
+				return -EINVAL;
+			}
+			evsel->core.attr.freq = 0;
+			evsel->core.attr.sample_period = 1;
+			evsel->needs_auxtrace_mmap = true;
+			rvtrace_evsel = evsel;
+			opts->full_auxtrace = true;
+		}
+	}
+
+	if (!opts->full_auxtrace)
+		return 0;
+
+	err = rvtrace_set_sink_attr(rvtrace_pmu, rvtrace_evsel);
+	if (err)
+		return err;
+
+	/* we are in snapshot mode */
+	if (opts->auxtrace_snapshot_mode) {
+		/*
+		 * No size were given to '-S' or '-m,', so go with
+		 * the default
+		 */
+		if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
+			if (privileged) {
+				opts->auxtrace_mmap_pages = MiB(4) / page_size;
+			} else {
+				opts->auxtrace_mmap_pages = KiB(128) / page_size;
+				if (opts->mmap_pages == UINT_MAX)
+					opts->mmap_pages = KiB(256) / page_size;
+			}
+		} else if (!opts->auxtrace_mmap_pages && !privileged &&
+						opts->mmap_pages == UINT_MAX) {
+			opts->mmap_pages = KiB(256) / page_size;
+		}
+
+		/*
+		 * '-m,xyz' was specified but no snapshot size, so make the
+		 * snapshot size as big as the auxtrace mmap area.
+		 */
+		if (!opts->auxtrace_snapshot_size) {
+			opts->auxtrace_snapshot_size =
+				opts->auxtrace_mmap_pages * (size_t)page_size;
+		}
+
+		/*
+		 * -Sxyz was specified but no auxtrace mmap area, so make the
+		 * auxtrace mmap area big enough to fit the requested snapshot
+		 * size.
+		 */
+		if (!opts->auxtrace_mmap_pages) {
+			size_t sz = opts->auxtrace_snapshot_size;
+
+			sz = round_up(sz, page_size) / page_size;
+			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
+		}
+
+		/* Snapshot size can't be bigger than the auxtrace area */
+		if (opts->auxtrace_snapshot_size >
+				opts->auxtrace_mmap_pages * (size_t)page_size) {
+			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
+			       opts->auxtrace_snapshot_size,
+			       opts->auxtrace_mmap_pages * (size_t)page_size);
+			return -EINVAL;
+		}
+
+		/* Something went wrong somewhere - this shouldn't happen */
+		if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
+			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
+			return -EINVAL;
+		}
+
+		pr_debug2("%s snapshot size: %zu\n", RVTRACE_PMU_NAME,
+			  opts->auxtrace_snapshot_size);
+	}
+
+	/* Buffer sizes weren't specified with '-m,xyz' so give some defaults */
+	if (!opts->auxtrace_mmap_pages) {
+		if (privileged) {
+			opts->auxtrace_mmap_pages = MiB(4) / page_size;
+		} else {
+			opts->auxtrace_mmap_pages = KiB(128) / page_size;
+			if (opts->mmap_pages == UINT_MAX)
+				opts->mmap_pages = KiB(256) / page_size;
+		}
+	}
+
+	/* Validate auxtrace_mmap_pages */
+	if (opts->auxtrace_mmap_pages) {
+		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
+		size_t min_sz;
+
+		if (opts->auxtrace_snapshot_mode)
+			min_sz = KiB(4);
+		else
+			min_sz = KiB(8);
+
+		if (sz < min_sz || !is_power_of_2(sz)) {
+			pr_err("Invalid mmap size for Intel Processor Trace: must be at least %zuKiB and a power of 2\n",
+			       min_sz / 1024);
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * To obtain the auxtrace buffer file descriptor, the auxtrace event
+	 * must come first.
+	 */
+	evlist__to_front(evlist, rvtrace_evsel);
+
+	/*
+	 * get the CPU on the sample - need it to associate trace ID in the
+	 * AUX_OUTPUT_HW_ID event, and the AUX event for per-cpu mmaps.
+	 */
+	evsel__set_sample_bit(rvtrace_evsel, CPU);
+
+	/* Add dummy event to keep tracking */
+	err = parse_event(evlist, "dummy:u");
+	if (err)
+		return err;
+
+	tracking_evsel = evlist__last(evlist);
+	evlist__set_tracking_event(evlist, tracking_evsel);
+
+	tracking_evsel->core.attr.freq = 0;
+	tracking_evsel->core.attr.sample_period = 1;
+
+	/* In per-cpu case, always need the time of mmap events etc */
+	if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus))
+		evsel__set_sample_bit(tracking_evsel, TIME);
+
+	return 0;
+}
+
+static int rvtrace_snapshot_start(struct auxtrace_record *itr)
+{
+	struct rvtrace_recording *ptr =
+			container_of(itr, struct rvtrace_recording, itr);
+	struct evsel *evsel;
+
+	evlist__for_each_entry(ptr->evlist, evsel) {
+		if (evsel->core.attr.type == ptr->rvtrace_pmu->type)
+			return evsel__disable(evsel);
+	}
+	return -EINVAL;
+}
+
+static int rvtrace_snapshot_finish(struct auxtrace_record *itr)
+{
+	struct rvtrace_recording *ptr =
+			container_of(itr, struct rvtrace_recording, itr);
+	struct evsel *evsel;
+
+	evlist__for_each_entry(ptr->evlist, evsel) {
+		if (evsel->core.attr.type == ptr->rvtrace_pmu->type)
+			return evsel__enable(evsel);
+	}
+	return -EINVAL;
+}
+
+static u64 rvtrace_reference(struct auxtrace_record *itr __maybe_unused)
+{
+	return rdtsc();
+}
+
+static void rvtrace_recording_free(struct auxtrace_record *itr)
+{
+	struct rvtrace_recording *ptr =
+			container_of(itr, struct rvtrace_recording, itr);
+
+	free(ptr);
+}
+
+static struct auxtrace_record *rvtrace_recording_init(int *err, struct perf_pmu *rvtrace_pmu)
+{
+	struct rvtrace_recording *ptr;
+
+	if (!rvtrace_pmu) {
+		*err = -ENODEV;
+		return NULL;
+	}
+
+	ptr = zalloc(sizeof(*ptr));
+	if (!ptr) {
+		*err = -ENOMEM;
+		return NULL;
+	}
+
+	ptr->rvtrace_pmu = rvtrace_pmu;
+	ptr->itr.parse_snapshot_options = rvtrace_parse_snapshot_options;
+	ptr->itr.recording_options = rvtrace_recording_options;
+	ptr->itr.info_priv_size = rvtrace_info_priv_size;
+	ptr->itr.info_fill = rvtrace_info_fill;
+	ptr->itr.snapshot_start = rvtrace_snapshot_start;
+	ptr->itr.snapshot_finish = rvtrace_snapshot_finish;
+	ptr->itr.free = rvtrace_recording_free;
+	ptr->itr.reference = rvtrace_reference;
+	ptr->itr.read_finish = auxtrace_record__read_finish;
+	ptr->itr.alignment = 0;
+
+	*err = 0;
+	return &ptr->itr;
+}
+
+static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus,
+					   int pmu_nr, struct evsel *evsel)
+{
+	int i;
+
+	if (!pmus)
+		return NULL;
+
+	for (i = 0; i < pmu_nr; i++) {
+		if (evsel->core.attr.type == pmus[i]->type)
+			return pmus[i];
+	}
+
+	return NULL;
+}
+
+struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, int *err)
+{
+	struct perf_pmu	*rvtrace_pmu = NULL;
+	struct perf_pmu *found_rvtrace = NULL;
+	struct evsel *evsel;
+
+	if (!evlist)
+		return NULL;
+
+	rvtrace_pmu = perf_pmus__find(RVTRACE_PMU_NAME);
+	evlist__for_each_entry(evlist, evsel) {
+		if (rvtrace_pmu && !found_rvtrace)
+			found_rvtrace = find_pmu_for_event(&rvtrace_pmu, 1, evsel);
+	}
+
+	if (found_rvtrace)
+		return rvtrace_recording_init(err, rvtrace_pmu);
+
+	*err = 0;
+	return NULL;
+}
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index a224687ffbc1..944a43d48739 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -1411,6 +1411,7 @@ int perf_event__process_auxtrace_info(const struct perf_tool *tool __maybe_unuse
 	case PERF_AUXTRACE_VPA_DTL:
 		err = powerpc_vpadtl_process_auxtrace_info(event, session);
 		break;
+	case PERF_AUXTRACE_RISCV_TRACE:
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index 6947f3f284c0..4f4714c1b53f 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -46,6 +46,7 @@ enum auxtrace_type {
 	PERF_AUXTRACE_S390_CPUMSF,
 	PERF_AUXTRACE_HISI_PTT,
 	PERF_AUXTRACE_VPA_DTL,
+	PERF_AUXTRACE_RISCV_TRACE,
 };
 
 enum itrace_period_type {
diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h
new file mode 100644
index 000000000000..1e48ed989dd7
--- /dev/null
+++ b/tools/perf/util/rvtrace.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#ifndef INCLUDE__UTIL_PERF_RVTRACE_H__
+#define INCLUDE__UTIL_PERF_RVTRACE_H__
+
+#include "debug.h"
+#include "auxtrace.h"
+#include "util/event.h"
+#include "util/session.h"
+#include <linux/bits.h>
+
+enum {
+	/* PMU->type (32 bit), total # of CPUs (32 bit) */
+	RVTRACE_PMU_TYPE_CPUS,
+	RVTRACE_HEADER_MAX,
+};
+
+/* Trace Encoder metadata */
+enum {
+	RVTRACE_ENCODER_CPU,
+	RVTRACE_ENCODER_NR_TRC_PARAMS,
+	RVTRACE_ENCODER_FORMAT,
+	RVTRACE_ENCODER_CONTEXT,
+	RVTRACE_ENCODER_INHB_SRC,
+	RVTRACE_ENCODER_SRCBITS,
+	RVTRACE_ENCODER_SRCID,
+	RVTRACE_ENCODER_PRIV_MAX,
+};
+
+#define RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH (RVTRACE_ENCODER_PRIV_MAX - RVTRACE_ENCODER_FORMAT)
+
+#define RVTRACE_HEADER_SIZE		(RVTRACE_HEADER_MAX * sizeof(u64))
+#define RVTRACE_ENCODER_PRIV_SIZE	(RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (8 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Zane Leung
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add support for RISC-V Nexus Trace decoder based on the reference
implementation from the RISC-V Nexus Trace specification. This includes
a CoreSight frame deformatter to remove the trace formatter overhead.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
 tools/perf/arch/riscv/util/auxtrace.c         |    1 +
 tools/perf/util/Build                         |    2 +
 tools/perf/util/nexus-rv-decoder/Build        |    1 +
 .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
 .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
 .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
 7 files changed, 2342 insertions(+)
 create mode 100644 tools/arch/riscv/include/asm/insn.h
 create mode 100644 tools/perf/util/nexus-rv-decoder/Build
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
 create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h

diff --git a/tools/arch/riscv/include/asm/insn.h b/tools/arch/riscv/include/asm/insn.h
new file mode 100644
index 000000000000..6c020afe926b
--- /dev/null
+++ b/tools/arch/riscv/include/asm/insn.h
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 SiFive
+ */
+
+#ifndef _ASM_RISCV_INSN_H
+#define _ASM_RISCV_INSN_H
+
+#include <linux/bits.h>
+
+#define RV_INSN_FUNCT3_MASK	GENMASK(14, 12)
+#define RV_INSN_FUNCT3_OPOFF	12
+#define RV_INSN_OPCODE_MASK	GENMASK(6, 0)
+#define RV_INSN_OPCODE_OPOFF	0
+#define RV_INSN_FUNCT12_OPOFF	20
+
+#define RV_ENCODE_FUNCT3(f_)	(RVG_FUNCT3_##f_ << RV_INSN_FUNCT3_OPOFF)
+#define RV_ENCODE_FUNCT12(f_)	(RVG_FUNCT12_##f_ << RV_INSN_FUNCT12_OPOFF)
+
+/* The bit field of immediate value in I-type instruction */
+#define RV_I_IMM_SIGN_OPOFF	31
+#define RV_I_IMM_11_0_OPOFF	20
+#define RV_I_IMM_SIGN_OFF	12
+#define RV_I_IMM_11_0_OFF	0
+#define RV_I_IMM_11_0_MASK	GENMASK(11, 0)
+
+/* The bit field of immediate value in J-type instruction */
+#define RV_J_IMM_SIGN_OPOFF	31
+#define RV_J_IMM_10_1_OPOFF	21
+#define RV_J_IMM_11_OPOFF	20
+#define RV_J_IMM_19_12_OPOFF	12
+#define RV_J_IMM_SIGN_OFF	20
+#define RV_J_IMM_10_1_OFF	1
+#define RV_J_IMM_11_OFF		11
+#define RV_J_IMM_19_12_OFF	12
+#define RV_J_IMM_10_1_MASK	GENMASK(9, 0)
+#define RV_J_IMM_11_MASK	GENMASK(0, 0)
+#define RV_J_IMM_19_12_MASK	GENMASK(7, 0)
+
+/*
+ * U-type IMMs contain the upper 20bits [31:20] of an immediate with
+ * the rest filled in by zeros, so no shifting required. Similarly,
+ * bit31 contains the signed state, so no sign extension necessary.
+ */
+#define RV_U_IMM_SIGN_OPOFF	31
+#define RV_U_IMM_31_12_OPOFF	0
+#define RV_U_IMM_31_12_MASK	GENMASK(31, 12)
+
+/* The bit field of immediate value in B-type instruction */
+#define RV_B_IMM_SIGN_OPOFF	31
+#define RV_B_IMM_10_5_OPOFF	25
+#define RV_B_IMM_4_1_OPOFF	8
+#define RV_B_IMM_11_OPOFF	7
+#define RV_B_IMM_SIGN_OFF	12
+#define RV_B_IMM_10_5_OFF	5
+#define RV_B_IMM_4_1_OFF	1
+#define RV_B_IMM_11_OFF		11
+#define RV_B_IMM_10_5_MASK	GENMASK(5, 0)
+#define RV_B_IMM_4_1_MASK	GENMASK(3, 0)
+#define RV_B_IMM_11_MASK	GENMASK(0, 0)
+
+/* The register offset in RVG instruction */
+#define RVG_RS1_OPOFF		15
+#define RVG_RS2_OPOFF		20
+#define RVG_RD_OPOFF		7
+#define RVG_RS1_MASK		GENMASK(4, 0)
+#define RVG_RS2_MASK		GENMASK(4, 0)
+#define RVG_RD_MASK		GENMASK(4, 0)
+
+/* The bit field of immediate value in RVC J instruction */
+#define RVC_J_IMM_SIGN_OPOFF	12
+#define RVC_J_IMM_4_OPOFF	11
+#define RVC_J_IMM_9_8_OPOFF	9
+#define RVC_J_IMM_10_OPOFF	8
+#define RVC_J_IMM_6_OPOFF	7
+#define RVC_J_IMM_7_OPOFF	6
+#define RVC_J_IMM_3_1_OPOFF	3
+#define RVC_J_IMM_5_OPOFF	2
+#define RVC_J_IMM_SIGN_OFF	11
+#define RVC_J_IMM_4_OFF		4
+#define RVC_J_IMM_9_8_OFF	8
+#define RVC_J_IMM_10_OFF	10
+#define RVC_J_IMM_6_OFF		6
+#define RVC_J_IMM_7_OFF		7
+#define RVC_J_IMM_3_1_OFF	1
+#define RVC_J_IMM_5_OFF		5
+#define RVC_J_IMM_4_MASK	GENMASK(0, 0)
+#define RVC_J_IMM_9_8_MASK	GENMASK(1, 0)
+#define RVC_J_IMM_10_MASK	GENMASK(0, 0)
+#define RVC_J_IMM_6_MASK	GENMASK(0, 0)
+#define RVC_J_IMM_7_MASK	GENMASK(0, 0)
+#define RVC_J_IMM_3_1_MASK	GENMASK(2, 0)
+#define RVC_J_IMM_5_MASK	GENMASK(0, 0)
+
+/* The bit field of immediate value in RVC B instruction */
+#define RVC_B_IMM_SIGN_OPOFF	12
+#define RVC_B_IMM_4_3_OPOFF	10
+#define RVC_B_IMM_7_6_OPOFF	5
+#define RVC_B_IMM_2_1_OPOFF	3
+#define RVC_B_IMM_5_OPOFF	2
+#define RVC_B_IMM_SIGN_OFF	8
+#define RVC_B_IMM_4_3_OFF	3
+#define RVC_B_IMM_7_6_OFF	6
+#define RVC_B_IMM_2_1_OFF	1
+#define RVC_B_IMM_5_OFF		5
+#define RVC_B_IMM_4_3_MASK	GENMASK(1, 0)
+#define RVC_B_IMM_7_6_MASK	GENMASK(1, 0)
+#define RVC_B_IMM_2_1_MASK	GENMASK(1, 0)
+#define RVC_B_IMM_5_MASK	GENMASK(0, 0)
+
+#define RVC_INSN_FUNCT4_MASK	GENMASK(15, 12)
+#define RVC_INSN_FUNCT4_OPOFF	12
+#define RVC_INSN_FUNCT3_MASK	GENMASK(15, 13)
+#define RVC_INSN_FUNCT3_OPOFF	13
+#define RVC_INSN_J_RS1_MASK	GENMASK(11, 7)
+#define RVC_INSN_J_RS2_MASK	GENMASK(6, 2)
+#define RVC_INSN_OPCODE_MASK	GENMASK(1, 0)
+#define RVC_ENCODE_FUNCT3(f_)	(RVC_FUNCT3_##f_ << RVC_INSN_FUNCT3_OPOFF)
+#define RVC_ENCODE_FUNCT4(f_)	(RVC_FUNCT4_##f_ << RVC_INSN_FUNCT4_OPOFF)
+
+/* The register offset in RVC op=C0 instruction */
+#define RVC_C0_RS1_OPOFF	7
+#define RVC_C0_RS2_OPOFF	2
+#define RVC_C0_RD_OPOFF		2
+
+/* The register offset in RVC op=C1 instruction */
+#define RVC_C1_RS1_OPOFF	7
+#define RVC_C1_RS2_OPOFF	2
+#define RVC_C1_RD_OPOFF		7
+
+/* The register offset in RVC op=C2 instruction */
+#define RVC_C2_RS1_OPOFF	7
+#define RVC_C2_RS2_OPOFF	2
+#define RVC_C2_RD_OPOFF		7
+#define RVC_C2_RS1_MASK		GENMASK(4, 0)
+
+/* parts of opcode for RVG*/
+#define RVG_OPCODE_FENCE	0x0f
+#define RVG_OPCODE_AUIPC	0x17
+#define RVG_OPCODE_BRANCH	0x63
+#define RVG_OPCODE_JALR		0x67
+#define RVG_OPCODE_JAL		0x6f
+#define RVG_OPCODE_SYSTEM	0x73
+#define RVG_SYSTEM_CSR_OFF	20
+#define RVG_SYSTEM_CSR_MASK	GENMASK(12, 0)
+
+/* parts of opcode for RVF, RVD and RVQ */
+#define RVFDQ_FL_FS_WIDTH_OFF	12
+#define RVFDQ_FL_FS_WIDTH_MASK	GENMASK(2, 0)
+#define RVFDQ_FL_FS_WIDTH_W	2
+#define RVFDQ_FL_FS_WIDTH_D	3
+#define RVFDQ_LS_FS_WIDTH_Q	4
+#define RVFDQ_OPCODE_FL		0x07
+#define RVFDQ_OPCODE_FS		0x27
+
+/* parts of opcode for RVV */
+#define RVV_OPCODE_VECTOR	0x57
+#define RVV_VL_VS_WIDTH_8	0
+#define RVV_VL_VS_WIDTH_16	5
+#define RVV_VL_VS_WIDTH_32	6
+#define RVV_VL_VS_WIDTH_64	7
+#define RVV_OPCODE_VL		RVFDQ_OPCODE_FL
+#define RVV_OPCODE_VS		RVFDQ_OPCODE_FS
+
+/* parts of opcode for RVC*/
+#define RVC_OPCODE_C0		0x0
+#define RVC_OPCODE_C1		0x1
+#define RVC_OPCODE_C2		0x2
+
+/* parts of funct3 code for I, M, A extension*/
+#define RVG_FUNCT3_JALR		0x0
+#define RVG_FUNCT3_BEQ		0x0
+#define RVG_FUNCT3_BNE		0x1
+#define RVG_FUNCT3_BLT		0x4
+#define RVG_FUNCT3_BGE		0x5
+#define RVG_FUNCT3_BLTU		0x6
+#define RVG_FUNCT3_BGEU		0x7
+
+/* parts of funct3 code for C extension*/
+#define RVC_FUNCT3_C_BEQZ	0x6
+#define RVC_FUNCT3_C_BNEZ	0x7
+#define RVC_FUNCT3_C_J		0x5
+#define RVC_FUNCT3_C_JAL	0x1
+#define RVC_FUNCT4_C_JR		0x8
+#define RVC_FUNCT4_C_JALR	0x9
+#define RVC_FUNCT4_C_EBREAK	0x9
+
+#define RVG_FUNCT12_ECALL	0x0
+#define RVG_FUNCT12_EBREAK	0x1
+#define RVG_FUNCT12_SRET	0x102
+#define RVG_FUNCT12_MRET	0x303
+
+#define RVG_MATCH_AUIPC		(RVG_OPCODE_AUIPC)
+#define RVG_MATCH_JALR		(RV_ENCODE_FUNCT3(JALR) | RVG_OPCODE_JALR)
+#define RVG_MATCH_JAL		(RVG_OPCODE_JAL)
+#define RVG_MATCH_FENCE		(RVG_OPCODE_FENCE)
+#define RVG_MATCH_BEQ		(RV_ENCODE_FUNCT3(BEQ) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BNE		(RV_ENCODE_FUNCT3(BNE) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BLT		(RV_ENCODE_FUNCT3(BLT) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BGE		(RV_ENCODE_FUNCT3(BGE) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BLTU		(RV_ENCODE_FUNCT3(BLTU) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BGEU		(RV_ENCODE_FUNCT3(BGEU) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_ECALL		(RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_EBREAK	(RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_SRET		(RV_ENCODE_FUNCT12(SRET) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_MRET		(RV_ENCODE_FUNCT12(MRET) | RVG_OPCODE_SYSTEM)
+#define RVC_MATCH_C_BEQZ	(RVC_ENCODE_FUNCT3(C_BEQZ) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_BNEZ	(RVC_ENCODE_FUNCT3(C_BNEZ) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_J		(RVC_ENCODE_FUNCT3(C_J) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_JAL		(RVC_ENCODE_FUNCT3(C_JAL) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_JR		(RVC_ENCODE_FUNCT4(C_JR) | RVC_OPCODE_C2)
+#define RVC_MATCH_C_JALR	(RVC_ENCODE_FUNCT4(C_JALR) | RVC_OPCODE_C2)
+#define RVC_MATCH_C_EBREAK	(RVC_ENCODE_FUNCT4(C_EBREAK) | RVC_OPCODE_C2)
+
+#define RVG_MASK_AUIPC		(RV_INSN_OPCODE_MASK)
+#define RVG_MASK_JALR		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_JAL		(RV_INSN_OPCODE_MASK)
+#define RVG_MASK_FENCE		(RV_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JALR		(RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JR		(RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JAL		(RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_J		(RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVG_MASK_BEQ		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BNE		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BLT		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BGE		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BLTU		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BGEU		(RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVC_MASK_C_BEQZ		(RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_BNEZ		(RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_EBREAK	0xffff
+#define RVG_MASK_EBREAK		0xffffffff
+#define RVG_MASK_ECALL		0xffffffff
+#define RVG_MASK_SRET		0xffffffff
+#define RVG_MASK_MRET		0xffffffff
+
+#define __INSN_LENGTH_MASK	_UL(0x3)
+#define __INSN_LENGTH_GE_32	_UL(0x3)
+#define __INSN_OPCODE_MASK	_UL(0x7F)
+#define __INSN_BRANCH_OPCODE	_UL(RVG_OPCODE_BRANCH)
+
+#define __RISCV_INSN_FUNCS(name, mask, val)				\
+static __always_inline bool riscv_insn_is_##name(u32 code)		\
+{									\
+	BUILD_BUG_ON(~(mask) & (val));					\
+	return (code & (mask)) == (val);				\
+}									\
+
+#if __riscv_xlen == 32
+/* C.JAL is an RV32C-only instruction */
+__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL)
+#else
+#define riscv_insn_is_c_jal(opcode) 0
+#endif
+__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC)
+__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR)
+__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL)
+__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J)
+__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ)
+__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE)
+__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT)
+__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE)
+__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU)
+__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU)
+__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ)
+__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ)
+__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK)
+__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
+__RISCV_INSN_FUNCS(ecall, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
+__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET)
+__RISCV_INSN_FUNCS(mret, RVG_MASK_MRET, RVG_MATCH_MRET)
+__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE);
+
+/* special case to catch _any_ system instruction */
+static __always_inline bool riscv_insn_is_system(u32 code)
+{
+	return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_SYSTEM;
+}
+
+/* special case to catch _any_ branch instruction */
+static __always_inline bool riscv_insn_is_branch(u32 code)
+{
+	return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_BRANCH;
+}
+
+static __always_inline bool riscv_insn_is_c_jr(u32 code)
+{
+	return (code & RVC_MASK_C_JR) == RVC_MATCH_C_JR &&
+	       (code & RVC_INSN_J_RS1_MASK) != 0;
+}
+
+static __always_inline bool riscv_insn_is_c_jalr(u32 code)
+{
+	return (code & RVC_MASK_C_JALR) == RVC_MATCH_C_JALR &&
+	       (code & RVC_INSN_J_RS1_MASK) != 0;
+}
+
+#define INSN_MATCH_LB		0x3
+#define INSN_MASK_LB		0x707f
+#define INSN_MATCH_LH		0x1003
+#define INSN_MASK_LH		0x707f
+#define INSN_MATCH_LW		0x2003
+#define INSN_MASK_LW		0x707f
+#define INSN_MATCH_LD		0x3003
+#define INSN_MASK_LD		0x707f
+#define INSN_MATCH_LBU		0x4003
+#define INSN_MASK_LBU		0x707f
+#define INSN_MATCH_LHU		0x5003
+#define INSN_MASK_LHU		0x707f
+#define INSN_MATCH_LWU		0x6003
+#define INSN_MASK_LWU		0x707f
+#define INSN_MATCH_SB		0x23
+#define INSN_MASK_SB		0x707f
+#define INSN_MATCH_SH		0x1023
+#define INSN_MASK_SH		0x707f
+#define INSN_MATCH_SW		0x2023
+#define INSN_MASK_SW		0x707f
+#define INSN_MATCH_SD		0x3023
+#define INSN_MASK_SD		0x707f
+
+#define INSN_MATCH_C_LD		0x6000
+#define INSN_MASK_C_LD		0xe003
+#define INSN_MATCH_C_SD		0xe000
+#define INSN_MASK_C_SD		0xe003
+#define INSN_MATCH_C_LW		0x4000
+#define INSN_MASK_C_LW		0xe003
+#define INSN_MATCH_C_SW		0xc000
+#define INSN_MASK_C_SW		0xe003
+#define INSN_MATCH_C_LDSP	0x6002
+#define INSN_MASK_C_LDSP	0xe003
+#define INSN_MATCH_C_SDSP	0xe002
+#define INSN_MASK_C_SDSP	0xe003
+#define INSN_MATCH_C_LWSP	0x4002
+#define INSN_MASK_C_LWSP	0xe003
+#define INSN_MATCH_C_SWSP	0xc002
+#define INSN_MASK_C_SWSP	0xe003
+
+#define INSN_OPCODE_MASK	0x007c
+#define INSN_OPCODE_SHIFT	2
+#define INSN_OPCODE_SYSTEM	28
+
+#define INSN_MASK_WFI		0xffffffff
+#define INSN_MATCH_WFI		0x10500073
+
+#define INSN_MASK_WRS		0xffffffff
+#define INSN_MATCH_WRS		0x00d00073
+
+#define INSN_MATCH_CSRRW	0x1073
+#define INSN_MASK_CSRRW		0x707f
+#define INSN_MATCH_CSRRS	0x2073
+#define INSN_MASK_CSRRS		0x707f
+#define INSN_MATCH_CSRRC	0x3073
+#define INSN_MASK_CSRRC		0x707f
+#define INSN_MATCH_CSRRWI	0x5073
+#define INSN_MASK_CSRRWI	0x707f
+#define INSN_MATCH_CSRRSI	0x6073
+#define INSN_MASK_CSRRSI	0x707f
+#define INSN_MATCH_CSRRCI	0x7073
+#define INSN_MASK_CSRRCI	0x707f
+
+#define INSN_MATCH_FLW		0x2007
+#define INSN_MASK_FLW		0x707f
+#define INSN_MATCH_FLD		0x3007
+#define INSN_MASK_FLD		0x707f
+#define INSN_MATCH_FLQ		0x4007
+#define INSN_MASK_FLQ		0x707f
+#define INSN_MATCH_FSW		0x2027
+#define INSN_MASK_FSW		0x707f
+#define INSN_MATCH_FSD		0x3027
+#define INSN_MASK_FSD		0x707f
+#define INSN_MATCH_FSQ		0x4027
+#define INSN_MASK_FSQ		0x707f
+
+#define INSN_MATCH_C_FLD	0x2000
+#define INSN_MASK_C_FLD		0xe003
+#define INSN_MATCH_C_FLW	0x6000
+#define INSN_MASK_C_FLW		0xe003
+#define INSN_MATCH_C_FSD	0xa000
+#define INSN_MASK_C_FSD		0xe003
+#define INSN_MATCH_C_FSW	0xe000
+#define INSN_MASK_C_FSW		0xe003
+#define INSN_MATCH_C_FLDSP	0x2002
+#define INSN_MASK_C_FLDSP	0xe003
+#define INSN_MATCH_C_FSDSP	0xa002
+#define INSN_MASK_C_FSDSP	0xe003
+#define INSN_MATCH_C_FLWSP	0x6002
+#define INSN_MASK_C_FLWSP	0xe003
+#define INSN_MATCH_C_FSWSP	0xe002
+#define INSN_MASK_C_FSWSP	0xe003
+
+#define INSN_MATCH_C_LHU		0x8400
+#define INSN_MASK_C_LHU			0xfc43
+#define INSN_MATCH_C_LH			0x8440
+#define INSN_MASK_C_LH			0xfc43
+#define INSN_MATCH_C_SH			0x8c00
+#define INSN_MASK_C_SH			0xfc43
+
+#define INSN_16BIT_MASK		0x3
+#define INSN_IS_16BIT(insn)	(((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK)
+#define INSN_LEN(insn)		(INSN_IS_16BIT(insn) ? 2 : 4)
+
+#define SHIFT_RIGHT(x, y)		\
+	((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
+
+#define REG_MASK			\
+	((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES))
+
+#define REG_OFFSET(insn, pos)		\
+	(SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK)
+
+#define REG_PTR(insn, pos, regs)	\
+	((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos)))
+
+#define GET_RS1(insn, regs)	(*REG_PTR(insn, SH_RS1, regs))
+#define GET_RS2(insn, regs)	(*REG_PTR(insn, SH_RS2, regs))
+#define GET_RS1S(insn, regs)	(*REG_PTR(RVC_RS1S(insn), 0, regs))
+#define GET_RS2S(insn, regs)	(*REG_PTR(RVC_RS2S(insn), 0, regs))
+#define GET_RS2C(insn, regs)	(*REG_PTR(insn, SH_RS2C, regs))
+#define GET_SP(regs)		(*REG_PTR(2, 0, regs))
+#define SET_RD(insn, regs, val)	(*REG_PTR(insn, SH_RD, regs) = (val))
+#define IMM_I(insn)		((s32)(insn) >> 20)
+#define IMM_S(insn)		(((s32)(insn) >> 25 << 5) | \
+				 (s32)(((insn) >> 7) & 0x1f))
+
+#define SH_RD			7
+#define SH_RS1			15
+#define SH_RS2			20
+#define SH_RS2C			2
+#define MASK_RX			0x1f
+
+#if defined(CONFIG_64BIT)
+#define LOG_REGBYTES		3
+#else
+#define LOG_REGBYTES		2
+#endif
+
+#define MASK_FUNCT3		0x7000
+
+#define GET_FUNCT3(insn)	(((insn) >> 12) & 7)
+
+#define RV_IMM_SIGN(x)		(-(((x) >> 31) & 1))
+#define RVC_IMM_SIGN(x)		(-(((x) >> 12) & 1))
+#define RV_X_MASK(X, s, mask)	(((X) >> (s)) & (mask))
+#define RV_X(X, s, n)		RV_X_MASK(X, s, ((1 << (n)) - 1))
+#define RVC_LW_IMM(x)		((RV_X(x, 6, 1) << 2) | \
+				 (RV_X(x, 10, 3) << 3) | \
+				 (RV_X(x, 5, 1) << 6))
+#define RVC_LD_IMM(x)		((RV_X(x, 10, 3) << 3) | \
+				 (RV_X(x, 5, 2) << 6))
+#define RVC_LWSP_IMM(x)		((RV_X(x, 4, 3) << 2) | \
+				 (RV_X(x, 12, 1) << 5) | \
+				 (RV_X(x, 2, 2) << 6))
+#define RVC_LDSP_IMM(x)		((RV_X(x, 5, 2) << 3) | \
+				 (RV_X(x, 12, 1) << 5) | \
+				 (RV_X(x, 2, 3) << 6))
+#define RVC_SWSP_IMM(x)		((RV_X(x, 9, 4) << 2) | \
+				 (RV_X(x, 7, 2) << 6))
+#define RVC_SDSP_IMM(x)		((RV_X(x, 10, 3) << 3) | \
+				 (RV_X(x, 7, 3) << 6))
+#define RVC_RS1S(insn)		(8 + RV_X(insn, SH_RD, 3))
+#define RVC_RS2S(insn)		(8 + RV_X(insn, SH_RS2C, 3))
+#define RVC_RS2(insn)		RV_X(insn, SH_RS2C, 5)
+#define RVC_X(X, s, mask)	RV_X_MASK(X, s, mask)
+
+#define RV_EXTRACT_FUNCT3(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RV_INSN_FUNCT3_OPOFF, \
+		   RV_INSN_FUNCT3_MASK >> RV_INSN_FUNCT3_OPOFF)); })
+
+#define RV_EXTRACT_RS1_REG(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); })
+
+#define RV_EXTRACT_RS2_REG(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RVG_RS2_OPOFF, RVG_RS2_MASK)); })
+
+#define RV_EXTRACT_RD_REG(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RVG_RD_OPOFF, RVG_RD_MASK)); })
+
+#define RV_EXTRACT_UTYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RV_U_IMM_31_12_OPOFF, RV_U_IMM_31_12_MASK)); })
+
+#define RV_EXTRACT_JTYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RV_J_IMM_10_1_OPOFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OFF) | \
+	(RV_X_MASK(x_, RV_J_IMM_11_OPOFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OFF) | \
+	(RV_X_MASK(x_, RV_J_IMM_19_12_OPOFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OFF) | \
+	(RV_IMM_SIGN(x_) << RV_J_IMM_SIGN_OFF); })
+
+#define RV_EXTRACT_ITYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RV_I_IMM_11_0_OPOFF, RV_I_IMM_11_0_MASK)) | \
+	(RV_IMM_SIGN(x_) << RV_I_IMM_SIGN_OFF); })
+
+#define RV_EXTRACT_BTYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RV_B_IMM_4_1_OPOFF, RV_B_IMM_4_1_MASK) << RV_B_IMM_4_1_OFF) | \
+	(RV_X_MASK(x_, RV_B_IMM_10_5_OPOFF, RV_B_IMM_10_5_MASK) << RV_B_IMM_10_5_OFF) | \
+	(RV_X_MASK(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \
+	(RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); })
+
+#define RVC_EXTRACT_C2_RS1_REG(x) \
+	({typeof(x) x_ = (x); \
+	(RV_X_MASK(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
+
+#define RVC_EXTRACT_JTYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RVC_X(x_, RVC_J_IMM_3_1_OPOFF, RVC_J_IMM_3_1_MASK) << RVC_J_IMM_3_1_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_4_OPOFF, RVC_J_IMM_4_MASK) << RVC_J_IMM_4_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_5_OPOFF, RVC_J_IMM_5_MASK) << RVC_J_IMM_5_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_6_OPOFF, RVC_J_IMM_6_MASK) << RVC_J_IMM_6_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_7_OPOFF, RVC_J_IMM_7_MASK) << RVC_J_IMM_7_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_9_8_OPOFF, RVC_J_IMM_9_8_MASK) << RVC_J_IMM_9_8_OFF) | \
+	(RVC_X(x_, RVC_J_IMM_10_OPOFF, RVC_J_IMM_10_MASK) << RVC_J_IMM_10_OFF) | \
+	(RVC_IMM_SIGN(x_) << RVC_J_IMM_SIGN_OFF); })
+
+#define RVC_EXTRACT_BTYPE_IMM(x) \
+	({typeof(x) x_ = (x); \
+	(RVC_X(x_, RVC_B_IMM_2_1_OPOFF, RVC_B_IMM_2_1_MASK) << RVC_B_IMM_2_1_OFF) | \
+	(RVC_X(x_, RVC_B_IMM_4_3_OPOFF, RVC_B_IMM_4_3_MASK) << RVC_B_IMM_4_3_OFF) | \
+	(RVC_X(x_, RVC_B_IMM_5_OPOFF, RVC_B_IMM_5_MASK) << RVC_B_IMM_5_OFF) | \
+	(RVC_X(x_, RVC_B_IMM_7_6_OPOFF, RVC_B_IMM_7_6_MASK) << RVC_B_IMM_7_6_OFF) | \
+	(RVC_IMM_SIGN(x_) << RVC_B_IMM_SIGN_OFF); })
+
+#define RVG_EXTRACT_SYSTEM_CSR(x) \
+	({typeof(x) x_ = (x); RV_X_MASK(x_, RVG_SYSTEM_CSR_OFF, RVG_SYSTEM_CSR_MASK); })
+
+#define RVFDQ_EXTRACT_FL_FS_WIDTH(x) \
+	({typeof(x) x_ = (x); RV_X_MASK(x_, RVFDQ_FL_FS_WIDTH_OFF, \
+				   RVFDQ_FL_FS_WIDTH_MASK); })
+
+#define RVV_EXTRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x)
+
+/*
+ * Get the immediate from a J-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_jtype_imm(u32 insn)
+{
+	return RV_EXTRACT_JTYPE_IMM(insn);
+}
+
+/*
+ * Update a J-type instruction with an immediate value.
+ *
+ * @insn: pointer to the jtype instruction
+ * @imm: the immediate to insert into the instruction
+ */
+static inline void riscv_insn_insert_jtype_imm(u32 *insn, s32 imm)
+{
+	/* drop the old IMMs, all jal IMM bits sit at 31:12 */
+	*insn &= ~GENMASK(31, 12);
+	*insn |= (RV_X_MASK(imm, RV_J_IMM_10_1_OFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OPOFF) |
+		 (RV_X_MASK(imm, RV_J_IMM_11_OFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OPOFF) |
+		 (RV_X_MASK(imm, RV_J_IMM_19_12_OFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OPOFF) |
+		 (RV_X_MASK(imm, RV_J_IMM_SIGN_OFF, 1) << RV_J_IMM_SIGN_OPOFF);
+}
+
+/*
+ * Put together one immediate from a U-type and I-type instruction pair.
+ *
+ * The U-type contains an upper immediate, meaning bits[31:12] with [11:0]
+ * being zero, while the I-type contains a 12bit immediate.
+ * Combined these can encode larger 32bit values and are used for example
+ * in auipc + jalr pairs to allow larger jumps.
+ *
+ * @utype_insn: instruction containing the upper immediate
+ * @itype_insn: instruction
+ * Return: combined immediate
+ */
+static inline s32 riscv_insn_extract_utype_itype_imm(u32 utype_insn, u32 itype_insn)
+{
+	s32 imm;
+
+	imm = RV_EXTRACT_UTYPE_IMM(utype_insn);
+	imm += RV_EXTRACT_ITYPE_IMM(itype_insn);
+
+	return imm;
+}
+
+/*
+ * Update a set of two instructions (U-type + I-type) with an immediate value.
+ *
+ * Used for example in auipc+jalrs pairs the U-type instructions contains
+ * a 20bit upper immediate representing bits[31:12], while the I-type
+ * instruction contains a 12bit immediate representing bits[11:0].
+ *
+ * This also takes into account that both separate immediates are
+ * considered as signed values, so if the I-type immediate becomes
+ * negative (BIT(11) set) the U-type part gets adjusted.
+ *
+ * @utype_insn: pointer to the utype instruction of the pair
+ * @itype_insn: pointer to the itype instruction of the pair
+ * @imm: the immediate to insert into the two instructions
+ */
+static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 *itype_insn, s32 imm)
+{
+	/* drop possible old IMM values */
+	*utype_insn &= ~(RV_U_IMM_31_12_MASK);
+	*itype_insn &= ~(RV_I_IMM_11_0_MASK << RV_I_IMM_11_0_OPOFF);
+
+	/* add the adapted IMMs */
+	*utype_insn |= (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1);
+	*itype_insn |= ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF);
+}
+
+/*
+ * Get the immediate from a B-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_btype_imm(u32 insn)
+{
+	return RV_EXTRACT_BTYPE_IMM(insn);
+}
+
+/*
+ * Get the immediate from a RVC B-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_cbtype_imm(u32 insn)
+{
+	return RVC_EXTRACT_BTYPE_IMM(insn);
+}
+
+/*
+ * Get the immediate from a RVC J-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_cjtype_imm(u16 insn)
+{
+	return RVC_EXTRACT_JTYPE_IMM(insn);
+}
+
+#endif /* _ASM_RISCV_INSN_H */
diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
index 5feee198ef97..a5450bef1a8a 100644
--- a/tools/perf/arch/riscv/util/auxtrace.c
+++ b/tools/perf/arch/riscv/util/auxtrace.c
@@ -10,6 +10,7 @@
 #include <linux/zalloc.h>
 #include <linux/string.h>
 #include <time.h>
+#include <errno.h>
 
 #include <internal/lib.h> // page_size
 #include "../../../util/auxtrace.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index bcccad7487a9..648a8552df9e 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -148,6 +148,8 @@ perf-util-y += cs-etm-decoder/
 endif
 perf-util-y += cs-etm-base.o
 
+perf-util-y += nexus-rv-decoder/
+
 perf-util-y += parse-branch-options.o
 perf-util-y += dump-insn.o
 perf-util-y += parse-regs-options.o
diff --git a/tools/perf/util/nexus-rv-decoder/Build b/tools/perf/util/nexus-rv-decoder/Build
new file mode 100644
index 000000000000..61cc960b1d04
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/Build
@@ -0,0 +1 @@
+perf-util-y += nexus-rv-decoder.o
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
new file mode 100644
index 000000000000..2f41d818ca32
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
@@ -0,0 +1,1364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/zalloc.h>
+#include <linux/types.h>
+#include <errno.h>
+#include <api/fs/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../debug.h"
+#include "../color.h"
+#include "../intlist.h"
+#include "nexus-rv-decoder.h"
+#include "nexus-rv-msg.h"
+#include "../../../arch/riscv/include/asm/insn.h"
+
+#define BUFFER_SIZE	1024
+
+#define RV_REGNO_ZERO   0
+#define RV_REGNO_RA	1
+
+static struct nexus_rv_defmt_buf *nexus_rv_get_buf(struct nexus_rv_defmt_buf defmt_bufs[],
+						   unsigned char id)
+{
+	if (defmt_bufs[id].buf != NULL)
+		return &defmt_bufs[id];
+
+	defmt_bufs[id].buf = (unsigned char *)malloc(BUFFER_SIZE);
+	if (!defmt_bufs[id].buf)
+		return NULL;
+
+	defmt_bufs[id].size = 0;
+	defmt_bufs[id].capacity = BUFFER_SIZE;
+
+	return &defmt_bufs[id];
+}
+
+static int nexus_rv_defmt_buf_append(struct nexus_rv_defmt_buf *defmt_buf, FILE *nexus,
+				     unsigned char data)
+{
+	if (defmt_buf->size == defmt_buf->capacity) {
+		defmt_buf->capacity *= 2;
+		defmt_buf->buf = realloc(defmt_buf->buf, defmt_buf->capacity);
+		if (!defmt_buf->buf)
+			return -ENOMEM;
+	}
+	defmt_buf->buf[defmt_buf->size++] = data;
+
+	if ((data & 0x3) == 0x3) {
+		size_t n = fwrite(defmt_buf->buf, defmt_buf->size, 1, nexus);
+
+		if (n != 1) {
+			pr_err("Encoder: failed to write nexus data\n");
+			return -EINVAL;
+		}
+		defmt_buf->size = 0;
+	}
+
+	return 0;
+}
+
+static size_t skip_frame_syncs(const unsigned char *buf, size_t len, size_t *fsync_count)
+{
+	const uint32_t FSYNC_PATTERN = 0x7FFFFFFF;    /* LE host pattern for Frame SYNC */
+	size_t skipped_bytes = 0;
+
+	while (skipped_bytes + 4 <= len) {
+		if (*((uint32_t *)(buf + skipped_bytes)) == FSYNC_PATTERN) {
+			skipped_bytes += 4;
+			(*fsync_count)++;
+		} else {
+			break;
+		}
+	}
+
+	return skipped_bytes;
+}
+
+/* remove coresight formatter frame */
+static int nexus_rv_pkt_defmt(struct nexus_rv_defmt_buf defmt_bufs[], FILE *nexus,
+			      const unsigned char *buf, size_t len)
+{
+	int err;
+	uint8_t flag_bit;
+	unsigned char data = 0;
+	int cur_id = -1, old_id = -1, new_id = -1;
+
+	/* 16 bytes is output by coresight trace formatter */
+	while (len >= 16) {
+		/* some linux drivers (e.g. for perf) will insert FSYNCS to pad or differentiate
+		 * between blocks of aligned data, always in frame aligned complete 16 byte frames.
+		 * we need to skip past these frames, resetting as we go.
+		 */
+		size_t fsync_count = 0;
+		size_t fsync_bytes = skip_frame_syncs(buf, len, &fsync_count);
+
+		if (fsync_bytes > 0) {
+			if (fsync_count % 4 == 0) {
+				cur_id = -1;
+				old_id = -1;
+				new_id = -1;
+			} else {
+				pr_err("Incorrect FSYNC reset pattern\n");
+				return -EINVAL;
+			}
+			buf += fsync_bytes;
+			len -= fsync_bytes;
+			continue;
+		}
+
+		flag_bit = 0x1;
+		for (int i = 0; i < 15; i += 2) {
+			if (buf[i] & 0x1) {
+				/* it's id */
+				old_id = new_id;
+				new_id = buf[i] >> 1; /* get new_id */
+				cur_id = (flag_bit & buf[15]) ? old_id : new_id;
+			} else {
+				/* it's data */
+				data = buf[i] | ((flag_bit & buf[15]) ? 0x1 : 0x0);
+			}
+
+			if (IS_VALID_ID(cur_id)) {
+				struct nexus_rv_defmt_buf *defmt_buf =
+					nexus_rv_get_buf(defmt_bufs, cur_id);
+
+				if (!defmt_buf)
+					return -ENOMEM;
+
+				/* it's data */
+				if ((buf[i] & 0x1) == 0) {
+					err = nexus_rv_defmt_buf_append(defmt_buf, nexus, data);
+					if (err)
+						return err;
+				}
+
+				/* buf[i+1] is alway data when i != 14 */
+				if (i != 14) {
+					err = nexus_rv_defmt_buf_append(defmt_buf, nexus,
+									buf[i + 1]);
+					if (err)
+						return err;
+				}
+			}
+
+			if (cur_id != new_id)
+				cur_id = new_id;
+
+			flag_bit <<= 1;
+		}
+		buf += 16;
+		len -= 16;
+	}
+	return 0;
+}
+
+/* dump all nexus messages (from nexus file) */
+static int nexus_rv_pkt_dump(struct nexus_rv_pkt_decoder *decoder, FILE *nexus)
+{
+	const char *color = PERF_COLOR_BLUE;
+	int fld_def  = -1;
+	int fld_bits = 0;
+	u64 fld_val = 0;
+
+	int msg_cnt    = 0;
+	int msg_bytes  = 0;
+	int msg_errors = 0;
+	int idle_cnt   = 0;
+
+	bool find_next_package = false;
+	int error_msgs = 0;
+
+	unsigned int tcode = 0;
+	unsigned int correlation_hist = 0;
+	unsigned int resourcefull_hrepeat = 0;
+
+	unsigned char msg_byte = 0;
+	unsigned char prev_byte = 0;
+	unsigned int mdo = 0;
+	unsigned int mseo = 0;
+
+	for (;;) {
+		prev_byte = msg_byte;
+		if (fread(&msg_byte, 1, 1, nexus) != 1)
+			break;  /* EOF */
+
+		if (find_next_package) {
+			if ((prev_byte & 0x3) == 0x3) { /* last byte */
+				error_msgs++;
+				find_next_package = false;
+
+				/* reinit some val */
+				fld_def = -1;
+				fld_bits = 0;
+				fld_val = 0;
+			} else {
+				continue;
+			}
+		}
+
+		/* This will skip long sequnece of idles (visible in true captures ...) */
+		if (msg_byte == 0xFF && prev_byte == 0xFF)
+			continue;
+
+		mdo  = msg_byte >> 2;
+		mseo = msg_byte & 0x3;
+
+		if (mseo == 0x2) {
+			pr_err(" ERROR: At offset %d: MSEO='10' is not allowed\n",
+				msg_bytes + idle_cnt);
+			find_next_package = true;
+			continue;
+		}
+
+		if (fld_def < 0) {
+			if (mseo == 0x3)  {
+				color_fprintf(stdout, color, "MSG #%d +%d - IDLE\n",
+					      msg_cnt, msg_bytes);
+				msg_cnt++;
+				msg_bytes++;
+				idle_cnt++;
+				continue;
+			}
+
+			if (mseo != 0x0) {
+				pr_err(" ERROR: At offset %d: Message must start from MSEO='00'\n",
+					      msg_bytes + idle_cnt);
+				find_next_package = true;
+				continue;
+			}
+
+			for (int d = 0; NEXUS_MSG_DEF[d].def != 0; d++) {
+				if ((NEXUS_MSG_DEF[d].def & 0x100) == 0)
+					continue;
+				if ((NEXUS_MSG_DEF[d].def & 0xFF) == mdo) {
+					fld_def = d; /* Found TCODE */
+					tcode = mdo;
+					break;
+				}
+			}
+
+			if (fld_def < 0) {
+				pr_err(" ERROR: At offset %d: Message with TCODE=%d is not defined for N-Trace\n",
+					msg_bytes + idle_cnt, mdo);
+				find_next_package = true;
+				continue;
+			}
+
+			color_fprintf(stdout, color, "MSG #%d +%d - %s TCODE[6]=%d",
+				      msg_cnt, msg_bytes, NEXUS_MSG_DEF[fld_def].name, mdo);
+			msg_cnt++;
+			msg_bytes++;
+
+			if (mdo == NEXUS_TCODE_Error)
+				msg_errors++;
+
+			fld_def++;
+			fld_bits = 0;
+			fld_val  = 0;
+			continue;
+		}
+
+		/* Accumulate 'mdo' to field value */
+		fld_val  |= (((u64)mdo) << fld_bits);
+		fld_bits += 6;
+
+		msg_bytes++;
+
+		/* Process fixed size fields (there may be more than one in one MDO record) */
+		while (NEXUS_MSG_DEF[fld_def].def & 0x200) {
+			int fld_size = NEXUS_MSG_DEF[fld_def].def & 0xFF;
+
+			if (fld_size & 0x80) {
+				/* Size of this field is defined by parameter ... */
+				fld_size = decoder->src_bits;
+			}
+			if (fld_bits < fld_size)
+				break;  /* Not enough bits for this field */
+			color_fprintf(stdout, color, " %s[%d]=0x%lx",
+				      NEXUS_MSG_DEF[fld_def].name, fld_size,
+				      fld_val & ((((u64)1) << fld_size) - 1));
+			fld_def++;
+			fld_val >>= fld_size;
+			fld_bits -= fld_size;
+		}
+
+		if (mseo == 0x0)
+			continue;
+
+		if (NEXUS_MSG_DEF[fld_def].def & 0x400) {
+			/* Process ResourceFull cfg HREPEAT field */
+			if (tcode == NEXUS_TCODE_ResourceFull) {
+				if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "RCODE")
+						&& fld_val == 0x2)
+					resourcefull_hrepeat = 1;
+
+				if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HREPEAT")
+						&& resourcefull_hrepeat != 1) {
+					/* no HREPEAT field */
+					fld_def++;
+					resourcefull_hrepeat = 0;
+				}
+			}
+
+			/* Process ProgTraceCorrelation cfg HIST field */
+			if (tcode == NEXUS_TCODE_ProgTraceCorrelation) {
+				if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "CDF") && fld_val == 0x2)
+					correlation_hist = 1;
+
+				if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HIST")
+						&& correlation_hist != 1) {
+					/* no HIST field */
+					fld_def++;
+					correlation_hist = 0;
+				}
+			}
+
+			/* Variable size field */
+			color_fprintf(stdout, color, " %s[%d]=0x%lx",
+				      NEXUS_MSG_DEF[fld_def].name, fld_bits, fld_val);
+
+			if (mseo == 3) {
+				printf("\n");
+				fld_def = -1;
+			} else {
+				fld_def++;
+			}
+			fld_bits = 0;
+			fld_val  = 0;
+			continue;
+		}
+
+		if (fld_bits > 0) {
+			pr_err(" ERROR: At offset %d: Not enough bits for non-variable field\n",
+				      msg_bytes + idle_cnt);
+			find_next_package = true;
+			continue;
+		}
+	}
+
+	color_fprintf(stdout, color,
+		      "\nStat: %d bytes, %d idles, %d messages, %d error messages, %d invalid messages",
+		      msg_bytes, idle_cnt, msg_cnt, msg_errors, error_msgs);
+	if (msg_cnt > 0)
+		color_fprintf(stdout, color, "%.2lf bytes/message", ((double)msg_bytes) / msg_cnt);
+
+	printf("\n");
+
+	return 0;
+}
+
+struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_decoder_params *params)
+{
+	struct nexus_rv_pkt_decoder *decoder;
+
+	if (!params)
+		return NULL;
+
+	decoder = zalloc(sizeof(struct nexus_rv_pkt_decoder));
+	if (!decoder)
+		return NULL;
+
+	decoder->formatted = params->formatted;
+	decoder->src_bits = params->src_bits;
+
+	return decoder;
+}
+
+void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder)
+{
+	for (int i = 0; i < MAX_ID; ++i)
+		free(decoder->defmt_bufs[i].buf);
+
+	free(decoder);
+}
+
+int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned char *buf, size_t len)
+{
+	int err;
+	char filename[PATH_MAX];
+	FILE *nexus;
+	char *dir = getenv("PERF_BUILDID_DIR");
+
+	/* TODO: The filename here is always trace.bin, which results in
+	 * only the last Nexus trace data being retained. A corresponding
+	 * Nexus filename should be generated for each AUX data.
+	 */
+	snprintf(filename, sizeof(filename), "%s/trace.bin", dir);
+	nexus = fopen(filename, "w+");
+
+	if (decoder->formatted) {
+		err = nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, len);
+		if (err) {
+			pr_err("Encoder: failed to remove coresight trace formatter\n");
+			return err;
+		}
+	} else {
+		size_t n = fwrite(buf, len, 1, nexus);
+
+		if (n != 1) {
+			pr_err("Encoder: failed to write nexus data\n");
+			fclose(nexus);
+			return -EINVAL;
+		}
+	}
+
+	fseek(nexus, 0, SEEK_SET);
+	err = nexus_rv_pkt_dump(decoder, nexus);
+
+	fclose(nexus);
+
+	return err;
+}
+
+static int nexus_rv_init_packet_buffer(struct nexus_rv_packet_buffer *packet_buffer)
+{
+	if (!packet_buffer->packets) {
+		packet_buffer->packets = calloc(BUFFER_SIZE, sizeof(struct nexus_rv_packet));
+		if (!packet_buffer->packets)
+			return -ENOMEM;
+	}
+
+	packet_buffer->size = 0;
+	packet_buffer->capacity = BUFFER_SIZE;
+
+	return 0;
+}
+
+static int nexus_rv_packet_buffer_append(struct nexus_rv_packet_buffer *packet_buffer,
+					 struct nexus_rv_packet packet)
+{
+	if (packet_buffer->size == packet_buffer->capacity) {
+		packet_buffer->capacity *= 2;
+		packet_buffer->packets = realloc(packet_buffer->packets,
+			sizeof(struct nexus_rv_packet) * packet_buffer->capacity);
+		if (!packet_buffer->packets)
+			return -ENOMEM;
+	}
+	packet_buffer->packets[packet_buffer->size++] = packet;
+
+	return 0;
+}
+
+static void nexus_rv_free_packet_buffer(struct nexus_rv_packet_buffer *packet_buffer)
+{
+	free(packet_buffer->packets);
+}
+
+static int nexus_rv_init_stack(struct nexus_rv_stack *stack)
+{
+	if (!stack->data) {
+		stack->data = (u64 *)malloc(sizeof(u64) * BUFFER_SIZE);
+		if (!stack->data)
+			return -ENOMEM;
+	}
+
+	stack->top = -1;
+	stack->capacity = BUFFER_SIZE;
+
+	return 0;
+}
+
+static int nexus_rv_stack_push(struct nexus_rv_stack *stack, u64 value)
+{
+	if (stack->top == stack->capacity - 1) {
+		stack->capacity *= 2;
+		stack->data = (u64 *)realloc(stack->data, sizeof(u64) * stack->capacity);
+		if (!stack->data)
+			return -ENOMEM;
+	}
+	stack->data[++stack->top] = value;
+
+	return 0;
+}
+
+static int nexus_rv_stack_pop(struct nexus_rv_stack *stack)
+{
+	u64 value;
+
+	if (stack->top == -1)
+		return 1;   /* Empty */
+
+	value = stack->data[stack->top--];
+	return value;
+}
+
+static void nexus_rv_free_stack(struct nexus_rv_stack *stack)
+{
+	free(stack->data);
+}
+
+static struct nexus_rv_src_context *nexus_rv_get_src_context(struct nexus_rv_insn_decoder *decoder,
+							     int src_id)
+{
+	struct int_node *node;
+	struct nexus_rv_src_context *ctx;
+
+	node = intlist__find(decoder->src_contexts, src_id);
+	if (node)
+		return (struct nexus_rv_src_context *)node->priv;
+
+	ctx = zalloc(sizeof(struct nexus_rv_src_context));
+	if (!ctx)
+		return NULL;
+
+	ctx->src_id = src_id;
+	ctx->nexdeco_pc = 1;
+	ctx->nexdeco_lastaddr = 1;
+	ctx->prv = 0;
+	ctx->v = 0;
+	ctx->timestamp = 0;
+	ctx->context = -1;
+	ctx->resourcefull_icnt = 0;
+
+	if (nexus_rv_init_stack(&ctx->stack)) {
+		free(ctx);
+		return NULL;
+	}
+
+	/* Add to RB Tree */
+	node = intlist__findnew(decoder->src_contexts, src_id);
+	if (!node) {
+		nexus_rv_free_stack(&ctx->stack);
+		free(ctx);
+		return NULL;
+	}
+
+	node->priv = ctx;
+
+	return ctx;
+}
+
+static void nexus_rv_free_src_contexts(struct intlist *src_contexts)
+{
+	struct int_node *node;
+
+	intlist__for_each_entry(node, src_contexts) {
+		struct nexus_rv_src_context *ctx = (struct nexus_rv_src_context *)node->priv;
+
+		nexus_rv_free_stack(&ctx->stack);
+		free(ctx);
+	}
+	intlist__delete(src_contexts);
+}
+
+static int handle_error_msg(struct nexus_rv_src_context *ctx, const char *err)
+{
+	pr_err("\nERROR: %s\n", err);
+	ctx->nexdeco_pc = 1; /* 1 means, that last address is unknown */
+	nexus_rv_init_stack(&ctx->stack);
+	return -EINVAL;
+}
+
+static int nexus_rv_insn_info_get(struct nexus_rv_insn_decoder *decoder, u8 *info, u64 *dest)
+{
+	u32 insn;
+	struct nexus_rv_src_context *ctx = decoder->current_src_ctx;
+	u64 addr = ctx->nexdeco_pc;
+
+	if (!decoder->mem_access(decoder->data, addr, ctx->prv, ctx->context,
+				 sizeof(insn), (u8 *)&insn))
+		return -EINVAL;
+
+	*info = INFO_LINEAR;
+	if ((insn & (__INSN_LENGTH_MASK)) != (__INSN_LENGTH_GE_32)) {
+		if (riscv_insn_is_c_beqz(insn) || riscv_insn_is_c_bnez(insn)) {
+			*info = INFO_BRANCH;
+			*dest = addr + riscv_insn_extract_cbtype_imm(insn);
+		} else if (riscv_insn_is_c_j(insn)) {
+			/* c.j offset */
+			*info = INFO_JUMP;
+			*dest = addr + riscv_insn_extract_cjtype_imm(insn);
+		} else if (riscv_insn_is_c_jr(insn)) {
+			*info = INFO_JUMP | INFO_INDIRECT;
+			/* ret => c.jr x1 */
+			if (RVC_EXTRACT_C2_RS1_REG(insn) == RV_REGNO_RA)
+				*info |= INFO_RET;
+		} else if (riscv_insn_is_c_jalr(insn)) {
+			*info = INFO_JUMP | INFO_CALL | INFO_INDIRECT;
+		} else {
+			*info = INFO_LINEAR;
+		}
+	} else {
+		if (riscv_insn_is_branch(insn)) {
+			*info = INFO_BRANCH;
+			*dest = addr + riscv_insn_extract_btype_imm(insn);
+		} else if (riscv_insn_is_jalr(insn)) {
+			*info = INFO_JUMP | INFO_INDIRECT;
+			if (RV_EXTRACT_RD_REG(insn) == RV_REGNO_ZERO
+					&& riscv_insn_extract_jtype_imm(insn) == 0) {
+				/* ret => jalr x0,x1,0 */
+				if (RV_EXTRACT_RS1_REG(insn) == RV_REGNO_RA)
+					*info |= INFO_RET;
+				else
+				/* jr rs1 => jalr x0,rs1,0 */
+					*info |= INFO_CALL;
+			}
+		} else if (riscv_insn_is_jal(insn)) {
+			*info = INFO_JUMP;
+			/* j offset => jal x0,offset */
+			if (RV_EXTRACT_RD_REG(insn) != RV_REGNO_ZERO)
+				*info |= INFO_CALL;
+			*dest = addr + riscv_insn_extract_jtype_imm(insn);
+		} else if (riscv_insn_is_sret(insn) || riscv_insn_is_mret(insn)) {
+			*info = INFO_JUMP | INFO_INDIRECT | INFO_RET;
+		} else if (riscv_insn_is_ecall(insn)) {
+			*info = INFO_JUMP | INFO_INDIRECT | INFO_CALL;
+		} else {
+			*info = INFO_LINEAR;
+		}
+		*info |= INFO_4;
+	}
+
+	pr_debug2(".nexdeco_pc=0x%lx .insn=0x%0*x\n", addr,
+		  (*info & INFO_4) ? 8 : 4,
+		  (*info & INFO_4) ? insn : (u16)insn);
+	return 0;
+}
+
+static int nexus_rv_emit_icnt(struct nexus_rv_insn_decoder *decoder, int n, u32 hist)
+{
+	u64 a = 0;
+	u32 hist_mask;
+	u8 info;
+	int done_icnt = 0; /* Number of done instruction count */
+	struct nexus_rv_src_context *ctx = decoder->current_src_ctx;
+
+	if (ctx->nexdeco_pc & 1)
+		return 0;  /* Not synchronized ... */
+
+	pr_debug2(".PC=0x%lX, EmitICNT(n=%d,hist=0x%x)\n", ctx->nexdeco_pc, n, hist);
+
+	/*
+	 * Adjust ICNT by what was handled by ResourceFull message[s]
+	 * before this message (message with normal ICNT field).
+	 * NOTE: ctx->resourcefull_icnt maybe positive or negative!
+	 */
+	if (n >= 0 && ctx->resourcefull_icnt != 0) {
+		n += ctx->resourcefull_icnt;
+		if (n < 0)
+			return handle_error_msg(ctx, "ICNT adjustment ERROR");
+
+		ctx->resourcefull_icnt = 0;    /* Make adjustment 'consumed' */
+	}
+
+	hist_mask = 0;  /* MSB is first in history, so we need sliding mask */
+	if (hist != 0) {
+		if (hist & (1 << (sizeof(u32) * 8 - 1))) {
+			hist_mask = (1 << (sizeof(u32) * 8 - 2));
+		} else {
+			hist_mask = 0x1;
+			while (hist_mask <= hist)
+				hist_mask <<= 1;
+			hist_mask >>= 2;
+		}
+	}
+
+	while (n != 0) {
+		ctx->current_pc = ctx->nexdeco_pc;
+		if (nexus_rv_insn_info_get(decoder, &info, &a))
+			return handle_error_msg(ctx, "failed to get insn info");
+
+		if (n > 0) {
+			n -= (info & INFO_4) ? 2 : 1;
+			if (n < 0)
+				return handle_error_msg(ctx, "ICNT too small");
+			done_icnt += 1;
+		}
+
+		if (info & INFO_CALL) {
+			u64 ret = ctx->nexdeco_pc + ((info & INFO_4) ? 4 : 2);
+
+			if (nexus_rv_stack_push(&ctx->stack, ret))
+				return handle_error_msg(ctx, "failed to push value to stack");
+		}
+
+		if (info & INFO_INDIRECT) { /* Cannot continue over indirect... */
+			if (info & INFO_RET) {
+				u64 ret = nexus_rv_stack_pop(&ctx->stack);
+
+				ctx->nexdeco_pc = ret;
+				if (n != 0) {
+					if (ret == 1)
+						return handle_error_msg(ctx,
+							"Not enough entries on callstack");
+					continue;
+				}
+			}
+
+			if (n > 0)
+				return handle_error_msg(ctx,
+							"indirect address encountered in ICNT");
+
+			break;
+		}
+
+		if (info & INFO_BRANCH) {
+			if (hist == 0) {
+				/* This is calling as DirectBranch */
+				if (n == 0)
+					info |= INFO_JUMP;  /* Force PC change below */
+			} else {
+				if (hist_mask & hist)
+					info |= INFO_JUMP;  /* Force PC change below */
+				hist_mask >>= 1;
+				if (hist_mask == 0 && n < 0)
+					n = 0;
+			}
+		}
+
+		if (info & INFO_JUMP)
+			ctx->nexdeco_pc = a;   /* Direct jump/call/branch */
+		else if (info & INFO_4)
+			ctx->nexdeco_pc += 4;
+		else
+			ctx->nexdeco_pc += 2;
+
+	}
+
+	return done_icnt;
+}
+
+static u64 nexus_rv_field_get(struct nexus_rv_insn_decoder *decoder, const char *name)
+{
+	for (int d = decoder->msg_field_pos; NEXUS_MSG_DEF[d].def != 1; d++) {
+		if (strcmp(NEXUS_MSG_DEF[d].name, name) == 0) {
+			int fi = d - decoder->msg_field_pos;
+
+			if (fi <= decoder->msg_field_cnt)
+				return decoder->msg_fields[fi];
+			return 0;
+		}
+	}
+	return 0;
+}
+
+#define NEX_FLDGET(n) nexus_rv_field_get(decoder, #n)
+
+static u64 nexus_rv_calculate_addr(u64 fu_addr, int full, u64 prev_addr)
+{
+	fu_addr <<= 1; /* LSB bit is never sent */
+	if ((fu_addr >> 32) & 0x10000)	    /* Perform MSB extension */
+		fu_addr |= 0xFFFF000000000000UL;
+	/* Update (NEW or XOR) */
+	if (!full) {
+		if (prev_addr & 1) /* Not Sync */
+			fu_addr = prev_addr;
+		else
+			fu_addr ^= prev_addr;
+	}
+	return fu_addr;
+}
+
+static int nexus_rv_msg_handle(struct nexus_rv_insn_decoder *decoder)
+{
+	int ret = 0;
+	u64 addr;
+	struct nexus_rv_packet packet;
+	int n = 0;
+	int new_src = 0;
+	u64 start_addr = 0;
+	int TCODE = decoder->msg_fields[0];
+
+	switch (TCODE) {
+	case NEXUS_TCODE_Ownership:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		decoder->current_src_ctx->prv = NEX_FLDGET(PRV);
+		decoder->current_src_ctx->v = NEX_FLDGET(V);
+		if (NEX_FLDGET(FORMAT) && NEX_FLDGET(CONTEXT))
+			decoder->current_src_ctx->context = NEX_FLDGET(CONTEXT);
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_DirectBranch:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_IndirectBranch:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+		addr = NEX_FLDGET(UADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 0,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_ProgTraceSync:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+		addr = NEX_FLDGET(FADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_DirectBranchSync:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+		addr = NEX_FLDGET(FADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_IndirectBranchSync:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+		addr = NEX_FLDGET(FADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_IndirectBranchHist:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST));
+
+		addr = NEX_FLDGET(UADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 0,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_IndirectBranchHistSync:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST));
+
+		addr = NEX_FLDGET(FADDR);
+		decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+			decoder->current_src_ctx->nexdeco_lastaddr);
+		pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+		decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+		decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_ResourceFull:
+		/* Determine repeat count (for RCODE=2) */
+		int hrepeat = 0;
+		int rcode = NEX_FLDGET(RCODE);
+
+		if (rcode == 2)
+			hrepeat = NEX_FLDGET(HREPEAT);
+
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		if (rcode == 1 || rcode == 2) {
+			int rdata = NEX_FLDGET(RDATA);
+
+			if (rdata > 1) {
+				/* Special calling to emit HIST only ... */
+				if (decoder->disp_hist_repeat) {
+					pr_debug2("RepeatHIST,0x%x,%d\n",
+						  rdata, decoder->disp_hist_repeat);
+					decoder->disp_hist_repeat = 0;
+				}
+				do {
+					/* ICNT is unknown (-1), what will process only HIST bits */
+					ret = nexus_rv_emit_icnt(decoder, -1, rdata);
+					if (ret < 0)
+						break;
+
+					/* Consume, so next time ICNT will be adjusted */
+					decoder->current_src_ctx->resourcefull_icnt -= ret;
+					hrepeat--;
+				} while (hrepeat > 0);
+			}
+		} else if (rcode == 0) {
+			decoder->current_src_ctx->resourcefull_icnt += NEX_FLDGET(RDATA);
+		}
+		break;
+
+	case NEXUS_TCODE_ProgTraceCorrelation:
+		int hist = 0;
+		int cdf = NEX_FLDGET(CDF);
+
+		if (cdf == 1)
+			hist = NEX_FLDGET(HIST);
+
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+
+		start_addr = decoder->current_src_ctx->nexdeco_pc;
+		n = NEX_FLDGET(ICNT);
+		ret = nexus_rv_emit_icnt(decoder, n, hist);
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_Error:
+		new_src = NEX_FLDGET(SRC);
+		if (new_src != decoder->current_src) {
+			struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+										    new_src);
+			if (!ctx)
+				return -ENOMEM;
+
+			decoder->current_src = new_src;
+			decoder->current_src_ctx = ctx;
+		}
+		decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+		break;
+
+	case NEXUS_TCODE_RepeatBranch:  /* Handled differently! */
+	default:
+		return -EINVAL;
+	}
+
+	if (ret >= 0) {
+		if (NEXUS_TCODE_Error == TCODE)
+			packet.sample_type = RVTRACE_LOSS;
+		else
+			packet.sample_type = ret ? RVTRACE_RANGE : RVTRACE_EMPTY;
+		packet.start_addr = start_addr;
+		packet.end_addr = decoder->current_src_ctx->current_pc;
+		packet.insn_cnt = ret;
+		packet.cpu = new_src;
+		packet.prv = decoder->current_src_ctx->prv;
+		packet.v = decoder->current_src_ctx->v;
+		packet.context = decoder->current_src_ctx->context;
+		packet.timestamp = decoder->current_src_ctx->timestamp;
+	} else {
+		packet.sample_type = RVTRACE_ERROR;
+	}
+
+	ret = nexus_rv_packet_buffer_append(&decoder->packet_buffer, packet);
+
+	return ret;
+}
+
+static int nexus_rv_insn_decode(struct nexus_rv_insn_decoder *decoder, FILE *nexus)
+{
+	int fld_def = -1;
+	int fld_bits = 0;
+	u64 fld_val = 0;
+
+	int msg_cnt = 0;
+	int msg_bytes = 0;
+	int msg_errors = 0;
+
+	bool find_next_package = false;
+
+	unsigned char msg_byte = 0;
+	unsigned char prev_byte = 0;
+
+	unsigned int mdo = 0;
+	unsigned int mseo = 0;
+
+	decoder->msg_field_cnt = 0;  /* No fields */
+
+	for (;;) {
+		prev_byte = msg_byte;
+		if (fread(&msg_byte, 1, 1, nexus) != 1)
+			break;	/* EOF */
+
+		if (find_next_package) {
+			if ((prev_byte & 0x3) == 0x3) { /* last byte */
+				find_next_package = false;
+
+				/* reinit some val */
+				fld_def = -1;
+				fld_bits = 0;
+				fld_val = 0;
+			} else {
+				continue;
+			}
+		}
+
+		/* This will skip long sequnece of idles (visible in true captures ...) */
+		if (msg_byte == 0xFF && prev_byte == 0xFF)
+			continue;
+
+		mdo = msg_byte >> 2;
+		mseo = msg_byte & 0x3;
+
+		if (mseo == 0x2) {
+			pr_err("ERROR: MSEO='10' is not allowed\n");
+			find_next_package = true;
+			continue;
+		}
+
+		if (fld_def < 0) {
+			if (mseo == 0x3)
+				continue;   /* skip idle */
+
+			if (mseo != 0x0) {
+				pr_err("ERROR: Message must start from MSEO='00'\n");
+				find_next_package = true;
+				continue;
+			}
+
+			for (int d = 0; NEXUS_MSG_DEF[d].def != 0; d++) {
+				if ((NEXUS_MSG_DEF[d].def & 0x100) == 0)
+					continue;
+				if ((NEXUS_MSG_DEF[d].def & 0xFF) == mdo) {
+					fld_def = d; /* Found TCODE */
+					break;
+				}
+			}
+
+			if (fld_def < 0) {
+				pr_err("ERROR: Message with TCODE=%d is not defined for RISC-V\n",
+					      mdo);
+				find_next_package = true;
+				continue;
+			}
+
+			/*
+			 * Special handling for RepeatBranch message.
+			 * We want to preserve previous packet, so we can
+			 * repeat it at end of RepeatBranch handling.
+			 */
+			if (mdo == NEXUS_TCODE_RepeatBranch) {
+				/* Save previous message fields */
+				decoder->saved_fields[0] = decoder->msg_field_pos;
+				decoder->saved_fields[1] = decoder->msg_field_cnt;
+				decoder->saved_fields[2] = decoder->msg_fields[0];
+				decoder->saved_fields[3] = decoder->msg_fields[1];
+				decoder->saved_fields[4] = decoder->msg_fields[2];
+				decoder->saved_fields[5] = decoder->msg_fields[3];
+				decoder->saved_fields[6] = decoder->msg_fields[4];
+			}
+
+			/* Save to allow later decoding */
+			decoder->msg_field_pos = fld_def;
+			decoder->msg_field_cnt = 0;
+			decoder->msg_fields[decoder->msg_field_cnt++] = mdo;
+
+			msg_cnt++;
+			msg_bytes++;
+
+			if (mdo == NEXUS_TCODE_Error)
+				msg_errors++;
+
+			fld_def++;
+			fld_bits = 0;
+			fld_val = 0;
+			continue;
+		}
+
+		/* Accumulate 'mdo' to field value */
+		fld_val |= (((u64)mdo) << fld_bits);
+		fld_bits += 6;
+
+		msg_bytes++;
+
+		/* Process fixed size fields (there may be more than one in one MDO record) */
+		while (NEXUS_MSG_DEF[fld_def].def & 0x200) {
+			int fld_size = NEXUS_MSG_DEF[fld_def].def & 0xFF;
+
+			if (fld_size & 0x80)
+				fld_size = decoder->src_bits;
+			if (fld_bits < fld_size)
+				break;	/* Not enough bits for this field */
+
+			/* Save field */
+			decoder->msg_fields[decoder->msg_field_cnt++] =
+				fld_val & ((((u64)1) << fld_size) - 1);
+
+			fld_def++;
+			fld_val >>= fld_size;
+			fld_bits -= fld_size;
+		}
+
+		if (mseo == 0x0)
+			continue;
+
+		if (NEXUS_MSG_DEF[fld_def].def & 0x400) {
+			/* Variable size field */
+			decoder->msg_fields[decoder->msg_field_cnt++] = fld_val; /* Save fieliid */
+
+			if (mseo == 3) {
+				int cnt = 1;
+
+				decoder->disp_hist_repeat = 0;
+
+				if (decoder->msg_fields[0] == NEXUS_TCODE_RepeatBranch) {
+					/* Special handling for repeat branch (which only
+					 * has 1 field!)
+					 */
+					/* Counter set in RepeatBranch message */
+					cnt = decoder->msg_fields[1];
+					decoder->msg_field_pos = decoder->saved_fields[0];
+					decoder->msg_field_cnt = decoder->saved_fields[1];
+					decoder->msg_fields[0] = decoder->saved_fields[2];
+					decoder->msg_fields[1] = decoder->saved_fields[3];
+					decoder->msg_fields[2] = decoder->saved_fields[4];
+					decoder->msg_fields[3] = decoder->saved_fields[5];
+					decoder->msg_fields[4] = decoder->saved_fields[6];
+					decoder->disp_hist_repeat = cnt;
+				}
+
+				while (cnt > 0) { /* Handle (1 or many times ...) */
+					int err = nexus_rv_msg_handle(decoder);
+
+					if (err < 0)
+						return err;
+					cnt--;
+				}
+
+				fld_def = -1;
+			} else {
+				fld_def++;
+			}
+			fld_bits = 0;
+			fld_val = 0;
+			continue;
+		}
+
+		if (fld_bits > 0) {
+			pr_err("Decode: Not enough bits for non-variable field\n");
+			find_next_package = true;
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new(struct nexus_rv_insn_decoder_params *params)
+{
+	int err;
+	struct nexus_rv_insn_decoder *decoder;
+
+	if (!params)
+		return NULL;
+
+	decoder = zalloc(sizeof(struct nexus_rv_insn_decoder));
+	if (!decoder)
+		return NULL;
+
+	decoder->mem_access = params->mem_access;
+	decoder->data = params->data;
+	decoder->formatted = params->formatted;
+	decoder->src_bits = params->src_bits;
+	decoder->current_src = -1;
+
+	decoder->src_contexts = intlist__new(NULL);
+	if (!decoder->src_contexts)
+		goto err_out;
+
+	err = nexus_rv_init_packet_buffer(&decoder->packet_buffer);
+	if (err)
+		goto err_out;
+
+	return decoder;
+
+err_out:
+	nexus_rv_free_src_contexts(decoder->src_contexts);
+	nexus_rv_free_packet_buffer(&decoder->packet_buffer);
+	free(decoder);
+	return NULL;
+}
+
+void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder)
+{
+	for (int i = 0; i < MAX_ID; ++i)
+		free(decoder->defmt_bufs[i].buf);
+
+	nexus_rv_free_src_contexts(decoder->src_contexts);
+	nexus_rv_free_packet_buffer(&decoder->packet_buffer);
+	free(decoder);
+}
+
+int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder)
+{
+	int err;
+	struct int_node *node;
+	struct nexus_rv_src_context *ctx;
+
+	if (decoder->src_contexts) {
+		intlist__for_each_entry(node, decoder->src_contexts) {
+			ctx = (struct nexus_rv_src_context *)node->priv;
+			ctx->nexdeco_pc = 1;
+			ctx->nexdeco_lastaddr = 1;
+			ctx->prv = 0;
+			ctx->v = 0;
+			ctx->timestamp = 0;
+			ctx->context = -1;
+			ctx->resourcefull_icnt = 0;
+			nexus_rv_init_stack(&ctx->stack);
+		}
+	}
+	decoder->current_src_ctx = NULL;
+	decoder->current_src = -1;
+
+	err = nexus_rv_init_packet_buffer(&decoder->packet_buffer);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder,
+			      const unsigned char *buf, size_t size)
+{
+	int err;
+	char filename[PATH_MAX];
+	FILE *nexus;
+	char *dir = getenv("PERF_BUILDID_DIR");
+
+	/* TODO: The filename here is always trace.bin, which results in
+	 * only the last Nexus trace data being retained. A corresponding
+	 * Nexus filename should be generated for each AUX data.
+	 */
+	snprintf(filename, sizeof(filename), "%s/trace.bin", dir);
+	nexus = fopen(filename, "w+");
+
+	if (decoder->formatted) {
+		err = nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, size);
+		if (err) {
+			pr_err("Encoder: failed to remove coresight trace formatter\n");
+			fclose(nexus);
+			return err;
+		}
+	} else {
+		size_t n = fwrite(buf, size, 1, nexus);
+
+		if (n != 1) {
+			pr_err("Encoder: failed to write nexus data\n");
+			fclose(nexus);
+			return -EINVAL;
+		}
+	}
+
+	fseek(nexus, 0, SEEK_SET);
+	err = nexus_rv_insn_decode(decoder, nexus);
+
+	fclose(nexus);
+
+	return err;
+}
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
new file mode 100644
index 000000000000..1595b59f3b3c
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ * Author: liangzhen <zhen.liang@spacemit.com>
+ */
+
+#ifndef INCLUDE__NEXUS_RV_DECODER_H__
+#define INCLUDE__NEXUS_RV_DECODER_H__
+
+#define INFO_LINEAR   0x1   // Linear (plain instruction or not taken BRANCH)
+#define INFO_4        0x2   // If not 4, it must be 2 on RISC-V
+//                    0x4   // Reserved (for exception or so ...)
+#define INFO_INDIRECT 0x8   // Possible for most types above
+#define INFO_BRANCH   0x10  // Always direct on RISC-V (may have LINEAR too)
+#define INFO_JUMP     0x20  // Direct or indirect
+#define INFO_CALL     0x40  // Direct or indirect (and always a jump)
+#define INFO_RET      0x80  // Return (always indirect and always a jump)
+
+#define MAX_ID 112  // Values of 0x00 and 0x70-0x7F are reserved by the ATB specification
+#define MSGFIELDS_MAX 10
+#define INSN_SZ       16
+
+/* check an ID is in the valid range */
+#define IS_VALID_ID(id)	    \
+	((id > 0) && (id < MAX_ID))
+
+struct rvtrace_queue;
+
+struct nexus_rv_defmt_buf {
+	unsigned char *buf;
+	size_t size;
+	size_t capacity;
+};
+
+struct nexus_rv_pkt_decoder {
+	bool formatted;
+	struct nexus_rv_defmt_buf defmt_bufs[MAX_ID];
+	u32 src_bits;
+};
+
+struct nexus_rv_pkt_decoder_params {
+	bool formatted;
+	u32 src_bits;
+};
+
+struct nexus_rv_stack {
+	u64 *data;
+	int top;
+	int capacity;
+};
+
+enum rvtrace_sample_type {
+	RVTRACE_EMPTY,
+	RVTRACE_RANGE,
+	RVTRACE_LOSS,
+	RVTRACE_ERROR,
+};
+
+enum riscv_privilege_mode {
+	RISCV_PRIV_USER_MODE,
+	RISCV_PRIV_SUPERVISOR_MODE,
+	RISCV_PRIV_MACHINE_MODE = 3,
+};
+
+typedef u32 (*rvtrace_mem_access_type)(void *, u64, enum riscv_privilege_mode,
+				       int, size_t, u8 *);
+
+struct nexus_rv_packet {
+	enum rvtrace_sample_type sample_type;
+	u64 start_addr;
+	u64 end_addr;
+	u32 insn_cnt;
+	int cpu;
+	enum riscv_privilege_mode prv;
+	bool v;
+	int context;
+	u64 timestamp;
+};
+
+struct nexus_rv_packet_buffer {
+	struct nexus_rv_packet *packets;
+	int size;
+	int capacity;
+};
+
+struct nexus_rv_src_context {
+	int src_id;
+	u64 nexdeco_pc;
+	u64 nexdeco_lastaddr;
+	u64 current_pc;
+	u64 timestamp;
+	enum riscv_privilege_mode prv;
+	bool v;
+	int context;
+	int resourcefull_icnt;
+	struct nexus_rv_stack stack;
+};
+
+struct nexus_rv_insn_decoder {
+	rvtrace_mem_access_type mem_access;
+	void *data;
+	bool formatted;
+	struct nexus_rv_defmt_buf defmt_bufs[MAX_ID];
+	u32 src_bits;
+	int msg_field_pos;
+	u64 msg_fields[MSGFIELDS_MAX];
+	u64 saved_fields[MSGFIELDS_MAX];
+	int msg_field_cnt;
+	int disp_hist_repeat;
+	int current_src;
+	struct intlist *src_contexts;
+	struct nexus_rv_src_context *current_src_ctx;
+	struct nexus_rv_packet_buffer packet_buffer;
+};
+
+struct nexus_rv_insn_decoder_params {
+	rvtrace_mem_access_type mem_access;
+	void *data;
+	bool formatted;
+	u32 src_bits;
+};
+
+struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_decoder_params *params);
+
+void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder);
+
+int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned char *buf, size_t len);
+
+struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new(
+	struct nexus_rv_insn_decoder_params *params);
+
+void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder);
+
+int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder);
+
+int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder,
+			      const unsigned char *buf, size_t size);
+
+#endif
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h b/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
new file mode 100644
index 000000000000..90d95f2c5f5f
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 IAR Systems AB.
+ */
+
+#ifndef INCLUDE__NEXUS_RV_MSG_H__
+#define INCLUDE__NEXUS_RV_MSG_H__
+
+/****************************************************************************/
+/* Nexus RISC-V Trace header. */
+
+// Header with common Nexus Trace definitions
+
+#include <stdint.h> // For uint32_t and uint64_t
+
+/****************************************************************************/
+/* Nexus specific values (based on Nexus Standard PDF) */
+
+#define NEXUS_FLDSIZE_TCODE                       6 // This is standard
+
+// Nexus TCODE values applicable to RISC-V
+#define NEXUS_TCODE_Ownership                     2
+#define NEXUS_TCODE_DirectBranch                  3
+#define NEXUS_TCODE_IndirectBranch                4
+#define NEXUS_TCODE_Error                         8
+#define NEXUS_TCODE_ProgTraceSync                 9
+#define NEXUS_TCODE_DirectBranchSync              11
+#define NEXUS_TCODE_IndirectBranchSync            12
+#define NEXUS_TCODE_ResourceFull                  27
+#define NEXUS_TCODE_IndirectBranchHist            28
+#define NEXUS_TCODE_IndirectBranchHistSync        29
+#define NEXUS_TCODE_RepeatBranch                  30
+#define NEXUS_TCODE_ProgTraceCorrelation          33
+
+// End of standard values
+/****************************************************************************/
+
+/****************************************************************************/
+/* RISC-V Nexus Trace related values (most 'recommended' by Nexus) */
+
+//  Sizes of fields:
+#define NEXUS_FLDSIZE_BTYPE     2 // Branch type
+#define NEXUS_FLDSIZE_SYNC      4 // Synchronization code
+#define NEXUS_FLDSIZE_ETYPE     4 // Error type
+#define NEXUS_FLDSIZE_EVCODE    4 // Event code (correlation)
+#define NEXUS_FLDSIZE_CDF       2
+#define NEXUS_FLDSIZE_RCODE     4 // Resource full code size
+//  from PROCESS fields:
+#define NEXUS_FLDSIZE_FORMAT    2
+#define NEXUS_FLDSIZE_PRV       2
+#define NEXUS_FLDSIZE_V         1
+
+#define NEXUS_PAR_SIZE_SRC      0 // SRC field size is defined by parameter #0
+
+#define NEXUS_HIST_BITS         31 // Number of valid HIST bits
+
+// Address skipping (on RISC-V LSB of PC is always 0, so it is not encoded)
+#define NEXUS_PARAM_AddrSkip    1
+#define NEXUS_PARAM_AddrUnit    1
+
+// End of RISC-V related values
+/****************************************************************************/
+
+// Nexus RISC-V Trace message definitions (dump & decode)
+
+// Macros to define Nexus Messages (NEXM_...)
+//  NOTE: These macros refer to 'NEXUS_TCODE_...' and 'NEXUS_FLDSIZE_...'
+//  It is also possible to NOT do it, but it provides less flexibility.
+//
+//                          name   def (marker | value)
+//#define NEXM_BEG(n, t)      {#n,    0x100 | (t)                 }
+#define NEXM_BEG(n)         {#n,    0x100 | (NEXUS_TCODE_##n)   } \
+			    // , NEXM_FLD_PAR(SRC) // SRC is always following TCODE
+//#define   NEXM_FLD(n, s)    {#n,    0x200 | (s)                 }
+#define   NEXM_FLD(n)       {#n,    0x200 | (NEXUS_FLDSIZE_##n) }
+// 0x80 means size is a parameter
+#define   NEXM_FLD_PAR(n)   {#n,    0x200 | 0x80 | (NEXUS_PAR_SIZE_##n) }
+#define   NEXM_VAR(n)       {#n,    0x400                       }
+#define   NEXM_ADR(n)       {#n,    0xC00                       }
+#define NEXM_END()          {NULL,  1                           }
+
+// Definition of Nexus Messages (subset applicable to RISC-V PC trace)
+static struct NEXM_MSGDEF_STRU {
+	const char *name; // Name of message/field
+	int def;          // Definition of field (see NEXM_... above)
+} NEXUS_MSG_DEF[] = {
+
+	NEXM_BEG(Ownership),
+	  NEXM_FLD_PAR(SRC),
+	  //NEXM_VAR(PROCESS),
+	  NEXM_FLD(FORMAT),
+	  NEXM_FLD(PRV),
+	  NEXM_FLD(V),
+	  NEXM_VAR(CONTEXT),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(DirectBranch),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_VAR(ICNT),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(IndirectBranch),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(BTYPE),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(UADDR),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(Error),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(ETYPE),
+	  NEXM_VAR(PAD),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(ProgTraceSync),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(SYNC),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(FADDR),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(DirectBranchSync),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(SYNC),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(FADDR),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(IndirectBranchSync),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(SYNC),
+	  NEXM_FLD(BTYPE),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(FADDR),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(ResourceFull),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(RCODE),
+	  NEXM_VAR(RDATA),
+	  NEXM_VAR(HREPEAT),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(IndirectBranchHist),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(BTYPE),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(UADDR),
+	  NEXM_VAR(HIST),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(IndirectBranchHistSync),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(SYNC),
+	  NEXM_FLD(BTYPE),
+	  // NEXM_FLD(CANCEL),
+	  NEXM_VAR(ICNT),
+	  NEXM_ADR(FADDR),
+	  NEXM_VAR(HIST),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(RepeatBranch),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_VAR(BCNT),
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	NEXM_BEG(ProgTraceCorrelation),
+	  NEXM_FLD_PAR(SRC),
+	  NEXM_FLD(EVCODE),
+	  NEXM_FLD(CDF),
+	  NEXM_VAR(ICNT),
+	  NEXM_VAR(HIST),   // Only if CDF=1!
+	  NEXM_VAR(TSTAMP),
+	NEXM_END(),
+
+	{ NULL, 0 } // End-marker ('def == 0' is not otherwise used - see NEXM_...)
+};
+
+#endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (9 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  3:41 ` [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace Zane Leung
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add PLT header and entry size definitions for RISC-V architecture.

RISC-V uses:
- PLT header size: 32 bytes
- PLT entry size: 16 bytes

This allows perf to correctly identify and display PLT symbols
instead of showing them as [unknown].

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/perf/util/symbol-elf.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 76912c62b6a0..e54ff59eb6ef 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -379,6 +379,10 @@ static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
 		*plt_header_size = 32;
 		*plt_entry_size = 16;
 		return true;
+	case EM_RISCV:
+		*plt_header_size = 32;
+		*plt_entry_size = 16;
+		return true;
 	case EM_SPARC:
 		*plt_header_size = 48;
 		*plt_entry_size = 12;
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (10 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Zane Leung
@ 2026-04-14  3:41 ` Zane Leung
  2026-04-14  4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
  2026-04-14  7:23 ` Anup Patel
  13 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  3:41 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

From: liangzhen <zhen.liang@spacemit.com>

Add RISC-V trace decoder implementation and integrate it into perf's
auxtrace infrastructure. This enables processing of RISC-V Nexus trace
data through the standard perf auxtrace pipeline.

Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
 tools/perf/util/Build             |    1 +
 tools/perf/util/auxtrace.c        |    3 +
 tools/perf/util/rvtrace-decoder.c | 1039 +++++++++++++++++++++++++++++
 tools/perf/util/rvtrace.h         |    2 +
 4 files changed, 1045 insertions(+)
 create mode 100644 tools/perf/util/rvtrace-decoder.c

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 648a8552df9e..5913eec9b847 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -148,6 +148,7 @@ perf-util-y += cs-etm-decoder/
 endif
 perf-util-y += cs-etm-base.o
 
+perf-util-y += rvtrace-decoder.o
 perf-util-y += nexus-rv-decoder/
 
 perf-util-y += parse-branch-options.o
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 944a43d48739..7539252a4543 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -56,6 +56,7 @@
 #include "s390-cpumsf.h"
 #include "util/mmap.h"
 #include "powerpc-vpadtl.h"
+#include "rvtrace.h"
 
 #include <linux/ctype.h>
 #include "symbol/kallsyms.h"
@@ -1412,6 +1413,8 @@ int perf_event__process_auxtrace_info(const struct perf_tool *tool __maybe_unuse
 		err = powerpc_vpadtl_process_auxtrace_info(event, session);
 		break;
 	case PERF_AUXTRACE_RISCV_TRACE:
+		err = rvtrace_process_auxtrace_info(event, session);
+		break;
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/rvtrace-decoder.c b/tools/perf/util/rvtrace-decoder.c
new file mode 100644
index 000000000000..ce103783d0c3
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder.c
@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ * Author: liangzhen <zhen.liang@spacemit.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+#include <sys/mman.h>
+
+#include <stdlib.h>
+
+#include "auxtrace.h"
+#include "color.h"
+#include "debug.h"
+#include "evlist.h"
+#include "intlist.h"
+#include "machine.h"
+#include "map.h"
+#include "perf.h"
+#include "session.h"
+#include "tool.h"
+#include "thread.h"
+#include "thread_map.h"
+#include "thread-stack.h"
+#include "util.h"
+#include "dso.h"
+#include "addr_location.h"
+#include <inttypes.h>
+#include "util/synthetic-events.h"
+#include "rvtrace.h"
+#include "nexus-rv-decoder/nexus-rv-decoder.h"
+#include "../../arch/riscv/include/asm/insn.h"
+
+struct rvtrace_auxtrace {
+	struct auxtrace auxtrace;
+	struct auxtrace_queues queues;
+	struct auxtrace_heap heap;
+	struct itrace_synth_opts synth_opts;
+	struct perf_session *session;
+	struct machine *machine;
+
+	u8 timeless_decoding;
+	u8 snapshot_mode;
+	u8 data_queued;
+
+	int num_cpu;
+	u64 latest_kernel_timestamp;
+	u32 auxtrace_type;
+	u64 branches_sample_type;
+	u64 branches_id;
+	u64 **metadata;
+	unsigned int pmu_type;
+};
+
+struct rvtrace_queue {
+	struct rvtrace_auxtrace *rvtrace;
+	struct thread *thread;
+	struct nexus_rv_insn_decoder *decoder;
+	struct auxtrace_buffer *buffer;
+	union perf_event *event_buf;
+	unsigned int queue_nr;
+	u64 offset;
+};
+
+static void rvtrace_set_thread(struct rvtrace_queue *rvtraceq,
+			       pid_t tid)
+{
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+
+	if (tid != -1) {
+		thread__zput(rvtraceq->thread);
+		rvtraceq->thread = machine__find_thread(rvtrace->machine, -1, tid);
+	}
+
+	/* Couldn't find a known thread */
+	if (!rvtraceq->thread)
+		rvtraceq->thread = machine__idle_thread(rvtrace->machine);
+}
+
+static u32 rvtrace_devmem_access(u64 address, size_t size, u8 *buffer)
+{
+	int fd;
+	void *map_base, *virt_addr;
+	u64 page_size = 4096, mapped_size = 4096;
+	u64 page_base = address & ~(page_size - 1);
+	u64 offset_in_page = address - page_base;
+	unsigned int width = 8 * size;
+
+	if (offset_in_page + width > page_size)
+		mapped_size *= 2;
+
+	fd = open("/dev/mem", O_RDONLY | O_SYNC);
+	if (fd < 0)
+		return 0;
+
+	map_base = mmap(NULL, mapped_size, PROT_READ, MAP_SHARED, fd, (off_t)(page_base));
+	if (map_base == MAP_FAILED) {
+		pr_debug("failed to mmap device address 0x%lx\n", address);
+		close(fd);
+		return 0;
+	}
+
+	virt_addr = (char *)map_base + offset_in_page;
+
+	/*
+	 * Note: higher versions of glibc use automatic vectorization by
+	 * default for memcpy, which can lead to incorrect memory results.
+	 */
+	/* memcpy(buffer, virt_addr, size); */
+	for (size_t i = 0; i < size; i++)
+		buffer[i] = ((volatile u8 *)virt_addr)[i];
+
+	munmap(map_base, mapped_size);
+	close(fd);
+	return size;
+}
+
+static u32 rvtrace_mem_access(void *data, u64 address, enum riscv_privilege_mode prv,
+			      int context, size_t size, u8 *buffer)
+{
+	u8 cpumode;
+	u64 offset;
+	int len;
+	struct machine *machine;
+	struct addr_location al;
+	struct dso *dso;
+	int ret = 0;
+	struct rvtrace_queue *rvtraceq = data;
+
+	if (!rvtraceq)
+		goto out;
+
+	addr_location__init(&al);
+
+	/* If the riscv_privilege_mode is machine mode, access physical address by /dev/mem */
+	if (prv == RISCV_PRIV_MACHINE_MODE)
+		return rvtrace_devmem_access(address, size, buffer);
+
+	machine = rvtraceq->rvtrace->machine;
+	if (address >= machine__kernel_start(machine))
+		cpumode = PERF_RECORD_MISC_KERNEL;
+	else
+		cpumode = PERF_RECORD_MISC_USER;
+
+	if (thread__tid(rvtraceq->thread) != context)
+		rvtrace_set_thread(rvtraceq, context);
+
+	if (!thread__find_map(rvtraceq->thread, cpumode, address, &al))
+		goto out;
+
+	dso = map__dso(al.map);
+	if (!dso)
+		goto out;
+
+	if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR &&
+	    dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE))
+		goto out;
+
+	offset = map__map_ip(al.map, address);
+
+	map__load(al.map);
+
+	len = dso__data_read_offset(dso, machine, offset, buffer, size);
+	if (len <= 0) {
+		ui__warning_once("RISC-V Nexus Trace: Missing DSO. Use 'perf archive' or debuginfod to export data from the traced system.\n"
+				 "                  Enable CONFIG_PROC_KCORE or use option '-k /path/to/vmlinux' for kernel symbols.\n");
+		if (!dso__auxtrace_warned(dso)) {
+			pr_err("RISC-V Nexus Trace: Debug data not found for address %#"PRIx64" in %s\n",
+				address,
+				dso__long_name(dso) ? dso__long_name(dso) : "Unknown");
+			dso__set_auxtrace_warned(dso);
+		}
+		goto out;
+	}
+	ret = len;
+out:
+	addr_location__exit(&al);
+	return ret;
+}
+
+
+static u8 rvtrace_cpu_mode(enum riscv_privilege_mode prv)
+{
+	u8 cpumode;
+
+	switch (prv) {
+	case RISCV_PRIV_USER_MODE:
+		cpumode = PERF_RECORD_MISC_USER;
+		break;
+	case RISCV_PRIV_SUPERVISOR_MODE:
+	case RISCV_PRIV_MACHINE_MODE:
+	default:
+		cpumode = PERF_RECORD_MISC_KERNEL;
+		break;
+	}
+	return cpumode;
+}
+
+static void rvtrace_synth_copy_insn(struct rvtrace_queue *rvtraceq,
+				    struct nexus_rv_packet *packet,
+				    struct perf_sample *sample)
+{
+	int ret;
+	u32 insn;
+
+	ret = rvtrace_mem_access(rvtraceq, sample->ip, packet->prv, packet->context,
+				 sizeof(insn), (u8 *)&insn);
+	if (!ret)
+		return;
+
+	sample->insn_len = ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_GE_32) ? 4 : 2;
+	memcpy(sample->insn, &insn, sample->insn_len);
+}
+
+static inline u64 rvtrace_resolve_sample_time(struct rvtrace_queue *rvtraceq,
+					      struct nexus_rv_packet *packet)
+{
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+
+	if (!rvtrace->timeless_decoding)
+		return packet->timestamp;
+	else
+		return rvtrace->latest_kernel_timestamp;
+}
+
+static int rvtrace_synth_branch_sample(struct rvtrace_queue *rvtraceq,
+				       struct nexus_rv_packet *packet)
+{
+	int ret = 0;
+	struct rvtrace_auxtrace *rvtrace = rvtraceq->rvtrace;
+	struct perf_sample sample;
+	union perf_event *event = rvtraceq->event_buf;
+
+	perf_sample__init(&sample, /*all=*/true);
+	event->sample.header.type = PERF_RECORD_SAMPLE;
+	event->sample.header.misc = rvtrace_cpu_mode(packet->prv);
+	event->sample.header.size = sizeof(struct perf_event_header);
+
+	if (thread__tid(rvtraceq->thread) != packet->context)
+		rvtrace_set_thread(rvtraceq, packet->context);
+
+	/* Set time field based on rvtrace auxtrace config. */
+	sample.time = rvtrace_resolve_sample_time(rvtraceq, packet);
+
+	sample.ip = packet->start_addr;
+	sample.pid = thread__pid(rvtraceq->thread);
+	sample.tid = thread__tid(rvtraceq->thread);
+	sample.addr = packet->end_addr;
+	sample.insn_cnt = packet->insn_cnt;
+	sample.id = rvtraceq->rvtrace->branches_id;
+	sample.stream_id = rvtraceq->rvtrace->branches_id;
+	sample.period = 1;
+	sample.cpu = packet->cpu;
+	sample.flags = 0;
+	sample.cpumode = event->sample.header.misc;
+
+	rvtrace_synth_copy_insn(rvtraceq, packet, &sample);
+
+	ret = perf_session__deliver_synth_event(rvtrace->session, event, &sample);
+	if (ret)
+		pr_err(
+		"RISC-V Trace: failed to deliver instruction event, error %d\n",
+		ret);
+	perf_sample__exit(&sample);
+
+	return ret;
+}
+
+static int rvtrace_synth_events(struct rvtrace_auxtrace *rvtrace,
+				struct perf_session *session)
+{
+	struct evlist *evlist = session->evlist;
+	struct evsel *evsel;
+	struct perf_event_attr attr;
+	bool found = false;
+	u64 id;
+	int err;
+
+	evlist__for_each_entry(evlist, evsel) {
+		if (evsel->core.attr.type == rvtrace->pmu_type) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_debug("No selected events with RISC-V Trace data\n");
+		return 0;
+	}
+
+	memset(&attr, 0, sizeof(struct perf_event_attr));
+	attr.size = sizeof(struct perf_event_attr);
+	attr.type = PERF_TYPE_HARDWARE;
+	attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
+	attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
+			    PERF_SAMPLE_PERIOD;
+	if (rvtrace->timeless_decoding)
+		attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
+	else
+		attr.sample_type |= PERF_SAMPLE_TIME;
+
+	attr.exclude_user = evsel->core.attr.exclude_user;
+	attr.exclude_kernel = evsel->core.attr.exclude_kernel;
+	attr.exclude_hv = evsel->core.attr.exclude_hv;
+	attr.exclude_host = evsel->core.attr.exclude_host;
+	attr.exclude_guest = evsel->core.attr.exclude_guest;
+	attr.sample_id_all = evsel->core.attr.sample_id_all;
+	attr.read_format = evsel->core.attr.read_format;
+
+	/* create new id val to be a fixed offset from evsel id */
+	id = evsel->core.id[0] + 1000000000;
+	if (!id)
+		id = 1;
+
+	if (rvtrace->synth_opts.branches) {
+		attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
+		attr.sample_period = 1;
+		attr.sample_type |= PERF_SAMPLE_ADDR;
+
+		err = perf_session__deliver_synth_attr_event(session, &attr, id);
+		if (err)
+			return err;
+		rvtrace->branches_sample_type = attr.sample_type;
+		rvtrace->branches_id = id;
+		id += 1;
+		attr.sample_type &= ~(u64)PERF_SAMPLE_ADDR;
+	}
+
+	return 0;
+}
+
+static int rvtrace_process_queue(struct rvtrace_queue *rvtraceq)
+{
+	struct nexus_rv_packet_buffer *packet_buffer = &rvtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct nexus_rv_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			rvtrace_synth_branch_sample(rvtraceq, &packet);
+		else if (packet.sample_type == RVTRACE_LOSS)
+			fprintf(stdout, "RISC-V Trace: A FIFO overrun has resulted in the loss of one or more messages\n");
+	}
+
+	return 0;
+}
+
+static int rvtrace_get_trace(struct rvtrace_queue *rvtraceq)
+{
+	struct auxtrace_buffer *aux_buffer = rvtraceq->buffer;
+	struct auxtrace_buffer *old_buffer = aux_buffer;
+	struct auxtrace_queue *queue;
+
+	queue = &rvtraceq->rvtrace->queues.queue_array[rvtraceq->queue_nr];
+
+	aux_buffer = auxtrace_buffer__next(queue, aux_buffer);
+
+	/* If no more data, drop the previous auxtrace_buffer and return */
+	if (!aux_buffer) {
+		if (old_buffer)
+			auxtrace_buffer__drop_data(old_buffer);
+		return 0;
+	}
+
+	rvtraceq->buffer = aux_buffer;
+
+	/* If the aux_buffer doesn't have data associated, try to load it */
+	if (!aux_buffer->data) {
+		/* get the file desc associated with the perf data file */
+		int fd = perf_data__fd(rvtraceq->rvtrace->session->data);
+
+		aux_buffer->data = auxtrace_buffer__get_data(aux_buffer, fd);
+		if (!aux_buffer->data)
+			return -ENOMEM;
+	}
+
+	/* If valid, drop the previous buffer */
+	if (old_buffer)
+		auxtrace_buffer__drop_data(old_buffer);
+
+	return aux_buffer->size;
+}
+
+/*
+ * rvtrace_get_data_block: Fetch a block from the auxtrace_buffer queue
+ *                         if need be.
+ * Returns:     < 0     if error
+ *              = 0     if no more auxtrace_buffer to read
+ *              > 0     if the current buffer isn't empty yet
+ */
+static int rvtrace_get_data_block(struct rvtrace_queue *rvtraceq)
+{
+	int ret;
+
+	ret = rvtrace_get_trace(rvtraceq);
+	if (ret <= 0)
+		return ret;
+
+	/*
+	 * We cannot assume consecutive blocks in the data file
+	 * are contiguous, reset the decoder to force re-sync.
+	 */
+	ret = nexus_rv_insn_decoder_reset(rvtraceq->decoder);
+	if (ret)
+		return ret;
+
+	return rvtraceq->buffer->size;
+}
+
+static int rvtrace_run_timeless_decoder(struct rvtrace_queue *rvtraceq)
+{
+	int ret;
+
+	while (1) {
+		ret = rvtrace_get_data_block(rvtraceq);
+		if (ret < 0)
+			return ret;
+
+		if (ret == 0)
+			break;
+
+		ret = nexus_rv_insn_decode_data(rvtraceq->decoder,
+						rvtraceq->buffer->data,
+						rvtraceq->buffer->size);
+		if (ret)
+			return ret;
+
+		ret = rvtrace_process_queue(rvtraceq);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int rvtrace_process_timeless_queues(struct rvtrace_auxtrace *rvtrace,
+					   pid_t tid)
+{
+	unsigned int i;
+	struct auxtrace_queues *queues = &rvtrace->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		struct auxtrace_queue *queue = &rvtrace->queues.queue_array[i];
+		struct rvtrace_queue *rvtraceq = queue->priv;
+
+		if (rvtraceq && ((tid == -1) || (queue->tid == tid))) {
+			rvtrace_set_thread(rvtraceq, queue->tid);
+			rvtrace_run_timeless_decoder(rvtraceq);
+		}
+	}
+
+	return 0;
+}
+
+static u64 rvtrace_queue_get_timestamp(struct rvtrace_queue *rvtraceq)
+{
+	struct nexus_rv_packet_buffer *packet_buffer = &rvtraceq->decoder->packet_buffer;
+
+	for (int i = 0; i < packet_buffer->size; i++) {
+		struct nexus_rv_packet packet = packet_buffer->packets[i];
+
+		if (packet.sample_type == RVTRACE_RANGE)
+			return packet.timestamp;
+	}
+
+	return 0;
+}
+
+static int rvtrace_queue_first_timestamp(struct rvtrace_auxtrace *rvtrace,
+					 struct rvtrace_queue *rvtraceq,
+					 unsigned int queue_nr)
+{
+	int ret;
+	u64 timestamp = 0;
+
+	/* Decode the first segment of data until a timestamp is found,
+	 * then add it to the heap for sorting by time across multiple
+	 * queues.
+	 */
+	while (1) {
+		/*
+		 * Fetch an aux_buffer from this rvtraceq. Bail if no more
+		 * blocks or an error has been encountered.
+		 */
+		ret = rvtrace_get_data_block(rvtraceq);
+		if (ret <= 0)
+			goto out;
+
+		ret = nexus_rv_insn_decode_data(rvtraceq->decoder,
+						rvtraceq->buffer->data,
+						rvtraceq->buffer->size);
+		if (ret)
+			goto out;
+
+		timestamp = rvtrace_queue_get_timestamp(rvtraceq);
+
+		/* We found a timestamp, no need to continue. */
+		if (timestamp)
+			break;
+	}
+
+	/* We have a timestamp and add it to the min heap */
+	ret = auxtrace_heap__add(&rvtrace->heap, queue_nr, timestamp);
+out:
+	return ret;
+}
+
+static int rvtrace_process_timestamped_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	int ret = 0;
+	unsigned int queue_nr, i;
+	struct auxtrace_queue *queue;
+	struct rvtrace_queue *rvtraceq;
+
+	/* First, find the first timestamp for each queue and add it to the heap. */
+	for (i = 0; i < rvtrace->queues.nr_queues; i++) {
+		queue = &rvtrace->queues.queue_array[i];
+		rvtraceq = queue->priv;
+		if (!rvtraceq)
+			continue;
+
+		rvtrace_set_thread(rvtraceq, queue->tid);
+
+		ret = rvtrace_queue_first_timestamp(rvtrace, rvtraceq, i);
+		if (ret)
+			return ret;
+	}
+
+	/* Process queues in the heap in timestamp order */
+	while (1) {
+		if (!rvtrace->heap.heap_cnt)
+			break;
+
+		/* Take the entry at the top of the min heap */
+		queue_nr = rvtrace->heap.heap_array[0].queue_nr;
+		queue = &rvtrace->queues.queue_array[queue_nr];
+		rvtraceq = queue->priv;
+
+		/*
+		 * Remove the top entry from the heap since we are about
+		 * to process it.
+		 */
+		auxtrace_heap__pop(&rvtrace->heap);
+
+		/*
+		 * Packets associated with this timestamp are already in
+		 * the rvtraceq->packet_buffer, so process them.
+		 */
+		ret = rvtrace_process_queue(rvtraceq);
+		if (ret)
+			return ret;
+
+		/*
+		 * Packets for this timestamp have been processed, time to
+		 * move on to the next timestamp, find the next timestamp
+		 * for this rvtraceq.
+		 */
+		ret = rvtrace_queue_first_timestamp(rvtrace, rvtraceq, queue_nr);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct rvtrace_queue *rvtrace_alloc_queue(struct rvtrace_auxtrace *rvtrace,
+						 unsigned int queue_nr)
+{
+	struct nexus_rv_insn_decoder_params params;
+	struct rvtrace_queue *rvtraceq;
+
+	rvtraceq = zalloc(sizeof(*rvtraceq));
+	if (!rvtraceq)
+		return NULL;
+
+	rvtraceq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
+	if (!rvtraceq->event_buf)
+		goto out_free;
+
+	rvtraceq->rvtrace = rvtrace;
+	rvtraceq->queue_nr = queue_nr;
+
+	params.mem_access = rvtrace_mem_access;
+	params.data = rvtraceq;
+	params.formatted = true;
+	if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC])
+		params.src_bits = rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS];
+
+	rvtraceq->decoder = nexus_rv_insn_decoder_new(&params);
+	if (!rvtraceq->decoder)
+		goto out_free;
+
+	rvtraceq->offset = 0;
+
+	return rvtraceq;
+
+out_free:
+	zfree(&rvtraceq->event_buf);
+	free(rvtraceq);
+
+	return NULL;
+}
+
+static int rvtrace_setup_queue(struct rvtrace_auxtrace *rvtrace,
+			       struct auxtrace_queue *queue,
+			       unsigned int queue_nr)
+{
+	struct rvtrace_queue *rvtraceq = queue->priv;
+
+	if (list_empty(&queue->head) || rvtraceq)
+		return 0;
+
+	rvtraceq = rvtrace_alloc_queue(rvtrace, queue_nr);
+
+	if (!rvtraceq)
+		return -ENOMEM;
+
+	queue->priv = rvtraceq;
+
+	return 0;
+}
+
+static int rvtrace_setup_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < rvtrace->queues.nr_queues; i++) {
+		ret = rvtrace_setup_queue(rvtrace, &rvtrace->queues.queue_array[i], i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rvtrace_update_queues(struct rvtrace_auxtrace *rvtrace)
+{
+	if (rvtrace->queues.new_data) {
+		rvtrace->queues.new_data = false;
+		return rvtrace_setup_queues(rvtrace);
+	}
+
+	return 0;
+}
+
+static int rvtrace_flush_events(struct perf_session *session,
+				const struct perf_tool *tool)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+					struct rvtrace_auxtrace,
+					auxtrace);
+	int ret;
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events)
+		return -EINVAL;
+
+	ret = rvtrace_update_queues(rvtrace);
+	if (ret < 0)
+		return ret;
+
+	if (rvtrace->timeless_decoding) {
+		/*
+		 * Pass tid = -1 to process all queues. But likely they will have
+		 * already been processed on PERF_RECORD_EXIT anyway.
+		 */
+		return rvtrace_process_timeless_queues(rvtrace, -1);
+	}
+
+	return rvtrace_process_timestamped_queues(rvtrace);
+}
+
+static void rvtrace_free_queue(void *priv)
+{
+	struct rvtrace_queue *rvtraceq = priv;
+
+	if (!rvtraceq)
+		return;
+
+	thread__zput(rvtraceq->thread);
+	nexus_rv_insn_decoder_free(rvtraceq->decoder);
+	zfree(&rvtraceq->event_buf);
+	free(rvtraceq);
+}
+
+static void rvtrace_free_events(struct perf_session *session)
+{
+	unsigned int i;
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+						    struct rvtrace_auxtrace,
+						    auxtrace);
+	struct auxtrace_queues *queues = &rvtrace->queues;
+
+	for (i = 0; i < queues->nr_queues; i++) {
+		rvtrace_free_queue(queues->queue_array[i].priv);
+		queues->queue_array[i].priv = NULL;
+	}
+
+	auxtrace_queues__free(queues);
+}
+
+static void rvtrace_free(struct perf_session *session)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+							struct rvtrace_auxtrace,
+							auxtrace);
+	rvtrace_free_events(session);
+	session->auxtrace = NULL;
+	for (int i = 0; i < rvtrace->num_cpu; i++)
+		zfree(&rvtrace->metadata[i]);
+
+	zfree(&rvtrace->metadata);
+	zfree(&rvtrace);
+}
+
+static bool rvtrace_evsel_is_auxtrace(struct perf_session *session,
+					     struct evsel *evsel)
+{
+	struct rvtrace_auxtrace *aux = container_of(session->auxtrace,
+						    struct rvtrace_auxtrace,
+						    auxtrace);
+
+	return evsel->core.attr.type == aux->pmu_type;
+}
+
+static int rvtrace_process_itrace_start(struct rvtrace_auxtrace *rvtrace,
+					union perf_event *event)
+{
+	struct thread *th;
+
+	if (rvtrace->timeless_decoding)
+		return 0;
+
+	/*
+	 * Add the tid/pid to the log so that we can get a match when
+	 * we get a contextID from the decoder.
+	 */
+	th = machine__findnew_thread(rvtrace->machine,
+				     event->itrace_start.pid,
+				     event->itrace_start.tid);
+	if (!th)
+		return -ENOMEM;
+
+	thread__put(th);
+
+	return 0;
+}
+
+static int rvtrace_process_event(struct perf_session *session,
+				 union perf_event *event,
+				 struct perf_sample *sample,
+				 const struct perf_tool *tool)
+{
+	int err = 0;
+	u64 timestamp;
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+							struct rvtrace_auxtrace,
+							auxtrace);
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events) {
+		pr_err("RISCV Trace requires ordered events\n");
+		return -EINVAL;
+	}
+
+	if (sample->time && (sample->time != (u64) -1))
+		timestamp = sample->time;
+	else
+		timestamp = 0;
+
+	if (timestamp || rvtrace->timeless_decoding) {
+		err = rvtrace_update_queues(rvtrace);
+		if (err)
+			return err;
+	}
+
+	switch (event->header.type) {
+	case PERF_RECORD_EXIT:
+		/*
+		 * Don't need to wait for rvtrace_flush_events() in per-thread/timeless
+		 * mode to start the decode because we know there will be no more trace
+		 * from this thread. All this does is emit samples earlier than waiting
+		 * for the flush in other modes, but with timestamps it makes sense to
+		 * wait for flush so that events from different threads are interleaved
+		 * properly.
+		 */
+		if (rvtrace->timeless_decoding)
+			return rvtrace_process_timeless_queues(rvtrace, event->fork.tid);
+		break;
+
+	case PERF_RECORD_ITRACE_START:
+		return rvtrace_process_itrace_start(rvtrace, event);
+
+	case PERF_RECORD_AUX:
+		/*
+		 * Record the latest kernel timestamp available for rollback when
+		 * no trace timestamp is available.
+		 */
+		if (timestamp)
+			rvtrace->latest_kernel_timestamp = timestamp;
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void rvtrace_dump(struct rvtrace_auxtrace *rvtrace,
+			 unsigned char *buf, size_t len)
+{
+	int ret;
+	struct nexus_rv_pkt_decoder_params params;
+	struct nexus_rv_pkt_decoder *decoder;
+	const char *color = PERF_COLOR_BLUE;
+
+	// TODO: Determine whether the trace data contains a CoreSight formatter frame
+	params.formatted = true;
+	if (!rvtrace->metadata[0][RVTRACE_ENCODER_INHB_SRC])
+		params.src_bits = rvtrace->metadata[0][RVTRACE_ENCODER_SRCBITS];
+
+	decoder = nexus_rv_pkt_decoder_new(&params);
+	if (!decoder) {
+		color_fprintf(stdout, color, " Failed to create decoder\n");
+		return;
+	}
+
+	color_fprintf(stdout, color,
+			". ... Trace Encoder Trace data: size %#zx bytes\n",
+			len);
+
+	ret = nexus_rv_pkt_desc(decoder, buf, len);
+	if (ret)
+		color_fprintf(stdout, color, " Bad packet!\n");
+
+	nexus_rv_pkt_decoder_free(decoder);
+}
+
+static void rvtrace_dump_event(struct rvtrace_auxtrace *rvtrace,
+			       unsigned char *buf, size_t len)
+{
+	printf(".\n");
+	rvtrace_dump(rvtrace, buf, len);
+}
+
+static int rvtrace_process_auxtrace_event(struct perf_session *session,
+					  union perf_event *event,
+					  const struct perf_tool *tool __maybe_unused)
+{
+	struct rvtrace_auxtrace *rvtrace = container_of(session->auxtrace,
+								 struct rvtrace_auxtrace,
+								 auxtrace);
+	if (!rvtrace->data_queued) {
+		struct auxtrace_buffer *buffer;
+		off_t  data_offset;
+		int fd = perf_data__fd(session->data);
+		bool is_pipe = perf_data__is_pipe(session->data);
+		int err;
+
+		if (is_pipe)
+			data_offset = 0;
+		else {
+			data_offset = lseek(fd, 0, SEEK_CUR);
+			if (data_offset == -1)
+				return -errno;
+		}
+
+		err = auxtrace_queues__add_event(&rvtrace->queues, session,
+						 event, data_offset, &buffer);
+
+		if (err)
+			return err;
+
+		if (dump_trace)
+			if (auxtrace_buffer__get_data(buffer, fd)) {
+				rvtrace_dump_event(rvtrace, buffer->data, buffer->size);
+				auxtrace_buffer__put_data(buffer);
+			}
+	}
+
+
+	return 0;
+}
+
+static bool rvtrace_is_timeless_decoding(struct rvtrace_auxtrace *rvtrace)
+{
+	struct evsel *evsel;
+	struct evlist *evlist = rvtrace->session->evlist;
+	bool timeless_decoding = true;
+
+	/*
+	 * Circle through the list of event and complain if we find one
+	 * with the time bit set.
+	 */
+	evlist__for_each_entry(evlist, evsel) {
+		if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
+			timeless_decoding = false;
+	}
+
+	return timeless_decoding;
+}
+
+static const char * const rvtrace_global_header_fmts[] = {
+	[RVTRACE_PMU_TYPE_CPUS]		    = "	    PMU type/num cpus	    %llx\n",
+};
+
+static const char * const rvtrace_encoder_priv_fmts[] = {
+	[RVTRACE_ENCODER_CPU]		    = "	    CPU			    %lld\n",
+	[RVTRACE_ENCODER_NR_TRC_PARAMS]	    = "	    NR_TRC_PARAMS	    %lld\n",
+	[RVTRACE_ENCODER_FORMAT]	    = "	    FORMAT		    %lld\n",
+	[RVTRACE_ENCODER_CONTEXT]	    = "	    CONTEXT		    %lld\n",
+	[RVTRACE_ENCODER_INHB_SRC]	    = "	    INHB_SRC		    %lld\n",
+	[RVTRACE_ENCODER_SRCBITS]	    = "	    SRCBITS		    %lld\n",
+	[RVTRACE_ENCODER_SRCID]		    = "	    SRCID		    %lld\n",
+};
+
+static void rvtrace_print_auxtrace_info(u64 *val, int num_cpu)
+{
+	int i, j, cpu = 0, nr_params = 0, fmt_offset = 0;
+
+	for (i = 0; i < RVTRACE_HEADER_MAX; i++)
+		fprintf(stdout, rvtrace_global_header_fmts[i], val[i]);
+
+	for (i = RVTRACE_HEADER_MAX; cpu < num_cpu; cpu++) {
+		fprintf(stdout, rvtrace_encoder_priv_fmts[RVTRACE_ENCODER_CPU], val[i++]);
+		nr_params = val[i++];
+		fmt_offset = RVTRACE_ENCODER_FORMAT;
+		for (j = fmt_offset; j < nr_params + fmt_offset; j++, i++)
+			fprintf(stdout, rvtrace_encoder_priv_fmts[j], val[i]);
+	}
+}
+
+int rvtrace_process_auxtrace_info(union perf_event *event,
+				  struct perf_session *session)
+{
+	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
+	struct rvtrace_auxtrace *rvtrace = NULL;
+	int err = 0;
+	int i;
+	int num_cpu = 0;
+	u64 *ptr = NULL;
+	u64 **metadata = NULL;
+
+	/* First the global part */
+	ptr = (u64 *) auxtrace_info->priv;
+	num_cpu = ptr[RVTRACE_PMU_TYPE_CPUS] & 0xffffffff;
+	metadata = zalloc(sizeof(*metadata) * num_cpu);
+	if (!metadata)
+		err = -ENOMEM;
+
+	/* Start parsing after the common part of the header */
+	i = RVTRACE_HEADER_MAX;
+
+	for (int j = 0; j < num_cpu; j++) {
+		metadata[j] = zalloc(sizeof(*metadata[j]) * RVTRACE_ENCODER_PRIV_MAX);
+		if (!metadata[j]) {
+			err = -ENOMEM;
+			goto err_free_metadata;
+		}
+
+		for (int k = 0; k < RVTRACE_ENCODER_PRIV_MAX; k++)
+			metadata[j][k] = ptr[i + k];
+		i += RVTRACE_ENCODER_PRIV_MAX;
+	}
+
+	rvtrace = zalloc(sizeof(*rvtrace));
+	if (!rvtrace) {
+		err = -ENOMEM;
+		goto err_free_metadata;
+	}
+
+	err = auxtrace_queues__init(&rvtrace->queues);
+	if (err)
+		goto err_free_rvtrace;
+
+	if (session->itrace_synth_opts->set) {
+		rvtrace->synth_opts = *session->itrace_synth_opts;
+	} else {
+		itrace_synth_opts__set_default(&rvtrace->synth_opts,
+			session->itrace_synth_opts->default_no_sample);
+		rvtrace->synth_opts.callchain = false;
+	}
+
+	rvtrace->session = session;
+	rvtrace->machine = &session->machines.host;
+	rvtrace->num_cpu = num_cpu;
+	rvtrace->pmu_type = (unsigned int) ((ptr[RVTRACE_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+	rvtrace->metadata = metadata;
+
+	rvtrace->auxtrace_type = auxtrace_info->type;
+	rvtrace->timeless_decoding = rvtrace_is_timeless_decoding(rvtrace);
+
+	rvtrace->auxtrace.process_event = rvtrace_process_event;
+	rvtrace->auxtrace.process_auxtrace_event = rvtrace_process_auxtrace_event;
+	rvtrace->auxtrace.flush_events = rvtrace_flush_events;
+	rvtrace->auxtrace.free_events = rvtrace_free_events;
+	rvtrace->auxtrace.free = rvtrace_free;
+	rvtrace->auxtrace.evsel_is_auxtrace = rvtrace_evsel_is_auxtrace;
+	session->auxtrace = &rvtrace->auxtrace;
+
+	if (dump_trace) {
+		rvtrace_print_auxtrace_info(ptr, num_cpu);
+		return 0;
+	}
+
+	err = rvtrace_synth_events(rvtrace, session);
+	if (err)
+		goto err_free_queues;
+
+	err = auxtrace_queues__process_index(&rvtrace->queues, session);
+	if (err)
+		goto err_free_queues;
+
+	rvtrace->data_queued = rvtrace->queues.populated;
+
+	return 0;
+
+err_free_queues:
+	auxtrace_queues__free(&rvtrace->queues);
+	session->auxtrace = NULL;
+err_free_rvtrace:
+	zfree(&rvtrace);
+err_free_metadata:
+	for (int j = 0; j < num_cpu; j++)
+		zfree(&metadata[j]);
+	zfree(&metadata);
+
+	return -EINVAL;
+}
diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h
index 1e48ed989dd7..84a59d3dc296 100644
--- a/tools/perf/util/rvtrace.h
+++ b/tools/perf/util/rvtrace.h
@@ -35,4 +35,6 @@ enum {
 #define RVTRACE_HEADER_SIZE		(RVTRACE_HEADER_MAX * sizeof(u64))
 #define RVTRACE_ENCODER_PRIV_SIZE	(RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
 
+int rvtrace_process_auxtrace_info(union perf_event *event, struct perf_session *session);
+
 #endif
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (11 preceding siblings ...)
  2026-04-14  3:41 ` [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace Zane Leung
@ 2026-04-14  4:15 ` Jie Gan
  2026-04-14  8:08   ` Zane Leung
  2026-04-14  7:23 ` Anup Patel
  13 siblings, 1 reply; 20+ messages in thread
From: Jie Gan @ 2026-04-14  4:15 UTC (permalink / raw)
  To: Zane Leung, robh, krzk+dt, conor+dt, palmer, pjw, gregkh,
	alexander.shishkin, irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin



On 4/14/2026 11:41 AM, Zane Leung wrote:
> From: liangzhen <zhen.liang@spacemit.com>
> 

I dont think you have looped relevant maintainers and reviewers for the 
Coresight subsystem.

Thanks,
Jie

> This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
> trace drivers within the CoreSight framework and integrating them with perf tools.
> The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
> components, which can be directly leveraged through the existing CoreSight infrastructure.
> 
> Linux RISC-V trace support form Anup Patel:
> (https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
> which currently lacks ATB component support and guidance on reusing CoreSight components.
> 
> The series includes:
> - RISC-V trace driver implementation within the CoreSight framework
> - RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
> - RISC-V trace PMU record capabilities and parsing events in perf.
> - RISC-V Nexus Trace decoder for perf tools
> 
> Any comments or suggestions are welcome.
> 
> Verification on K3 SoC:
> To verify this patch series on K3 hardware, the following device tree are required:
> 1. RISC-V Trace Encoder node (8)
> 2. RISC-V ATB Bridge node (8)
> 3. RISC-V Trace Funnel node (2)
> 3. CoreSight Funnel configuration for RISC-V (1)
> 4. CoreSight TMC configuration for trace buffer (1)
> 
> /{
>          dummy_clk: apb-pclk {
>                  compatible = "fixed-clock";
>                  #clock-cells = <0x0>;
>                  clock-output-names = "clk14mhz";
>                  clock-frequency = <14000000>;
>          };
> 
> 
>          soc: soc {
>                  #address-cells = <2>;
>                  #size-cells = <2>;
> 
>                  encoder0: encoder@d9002000 {
>                          compatible = "riscv,trace-encoder";
>                          reg = <0x0 0xd9002000 0x0 0x1000>;
>                          cpus = <&cpu_0>;
>                          out-ports {
>                                  port {
>                                          cluster0_encoder0_out_port: endpoint {
>                                                  remote-endpoint = <&cluster0_bridge0_in_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
>                  bridge0: bridge@d9003000 {
>                          compatible = "riscv,trace-atbbridge";
>                          reg = <0x0 0xd9003000 0x0 0x1000>;
>                          cpus = <&cpu_0>;
>                          out-ports {
>                                  port {
>                                          cluster0_bridge0_out_port: endpoint {
>                                                  remote-endpoint = <&cluster0_funnel_in_port0>;
>                                          };
>                                  };
>                          };
>                          in-ports {
>                                  port {
>                                          cluster0_bridge0_in_port: endpoint {
>                                                  remote-endpoint = <&cluster0_encoder0_out_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
> ...
> 
>                  cluster0_funnel: funnel@d9000000 {
>                          compatible = "riscv,trace-funnel";
>                          reg = <0x0 0xd9000000 0x0 0x1000>;
>                          cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
>                          riscv,timestamp-present;
>                          out-ports {
>                                  port {
>                                          cluster0_funnel_out_port: endpoint {
>                                                  remote-endpoint = <&main_funnel_in_port0>;
>                                          };
>                                  };
>                          };
>                          in-ports {
>                                  #address-cells = <1>;
>                                  #size-cells = <0>;
> 
>                                  port@0 {
>                                          reg = <0>;
>                                          cluster0_funnel_in_port0: endpoint {
>                                                  remote-endpoint = <&cluster0_bridge0_out_port>;
>                                          };
>                                  };
> 
>                                  port@1 {
>                                          reg = <1>;
>                                          cluster0_funnel_in_port1: endpoint {
>                                                  remote-endpoint = <&cluster0_bridge1_out_port>;
>                                          };
>                                  };
> 
>                                  port@2 {
>                                          reg = <2>;
>                                          cluster0_funnel_in_port2: endpoint {
>                                                  remote-endpoint = <&cluster0_bridge2_out_port>;
>                                          };
>                                  };
> 
>                                  port@3 {
>                                          reg = <3>;
>                                          cluster0_funnel_in_port3: endpoint {
>                                                  remote-endpoint = <&cluster0_bridge3_out_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
>                  cluster1_funnel: funnel@d9010000 {
>                          compatible = "riscv,trace-funnel";
>                          reg = <0x0 0xd9010000 0x0 0x1000>;
>                          cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
>                          riscv,timestamp-present;
>                          out-ports {
>                                  port {
>                                          cluster1_funnel_out_port: endpoint {
>                                                  remote-endpoint = <&main_funnel_in_port1>;
>                                          };
>                                  };
>                          };
>                          in-ports {
>                                  #address-cells = <1>;
>                                  #size-cells = <0>;
> 
>                                  port@0 {
>                                          reg = <0>;
>                                          cluster1_funnel_in_port0: endpoint {
>                                                  remote-endpoint = <&cluster1_bridge0_out_port>;
>                                          };
>                                  };
> 
>                                  port@1 {
>                                          reg = <1>;
>                                          cluster1_funnel_in_port1: endpoint {
>                                                  remote-endpoint = <&cluster1_bridge1_out_port>;
>                                          };
>                                  };
> 
>                                  port@2 {
>                                          reg = <2>;
>                                          cluster1_funnel_in_port2: endpoint {
>                                                  remote-endpoint = <&cluster1_bridge2_out_port>;
>                                          };
>                                  };
> 
>                                  port@3 {
>                                          reg = <3>;
>                                          cluster1_funnel_in_port3: endpoint {
>                                                  remote-endpoint = <&cluster1_bridge3_out_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
>                  main_funnel: funnel@d9042000 {
>                          compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
>                          reg = <0x0 0xd9042000 0x0 0x1000>;
>                          clocks = <&dummy_clk>;
>                          clock-names = "apb_pclk";
>                          out-ports {
>                                  port {
>                                          main_funnel_out_port: endpoint {
>                                                  remote-endpoint = <&etf_in_port>;
>                                          };
>                                  };
>                          };
>                          in-ports {
>                                  #address-cells = <1>;
>                                  #size-cells = <0>;
> 
>                                  port@0 {
>                                          reg = <0>;
>                                          main_funnel_in_port0: endpoint {
>                                                  remote-endpoint = <&cluster0_funnel_out_port>;
>                                          };
>                                  };
> 
>                                  port@1 {
>                                          reg = <1>;
>                                          main_funnel_in_port1: endpoint {
>                                                  remote-endpoint = <&cluster1_funnel_out_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
>                  etf: etf@d9043000 {
>                          compatible = "arm,coresight-tmc", "arm,primecell";
>                          reg = <0x0 0xd9043000 0x0 0x1000>;
>                          clocks = <&dummy_clk>;
>                          clock-names = "apb_pclk";
>                          out-ports {
>                                  port {
>                                          etf_out_port: endpoint {
>                                                  remote-endpoint = <&etr_in_port>;
>                                          };
>                                  };
>                          };
>                          in-ports {
>                                  port {
>                                          etf_in_port: endpoint {
>                                                  remote-endpoint = <&main_funnel_out_port>;
>                                          };
>                                  };
>                          };
>                  };
> 
>                  etr: etr@d9044000 {
>                          compatible = "arm,coresight-tmc", "arm,primecell";
>                          reg = <0x0 0xd9044000 0x0 0x1000>;
>                          clocks = <&dummy_clk>;
>                          clock-names = "apb_pclk";
>                          arm,scatter-gather;
>                          in-ports {
>                                  port {
>                                          etr_in_port: endpoint {
>                                                  remote-endpoint = <&etf_out_port>;
>                                          };
>                                  };
>                          };
>                  };
>          };
> };
> 
> Verification case:
> 
> ~ # perf list pmu
>    rvtrace//                                          [Kernel PMU event]
> 
> ~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
> Linux
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.191 MB perf.data ]
> ~ # perf script
>             uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
>             uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
> ...
> 
> liangzhen (12):
>    coresight: Add RISC-V support to CoreSight tracing
>    coresight: Initial implementation of RISC-V trace driver
>    coresight: Add RISC-V Trace Encoder driver
>    coresight: Add RISC-V Trace Funnel driver
>    coresight: Add RISC-V Trace ATB Bridge driver
>    coresight rvtrace: Add timestamp component support for encoder and
>      funnel
>    coresight: Add RISC-V PMU name support
>    perf tools: riscv: making rvtrace PMU listable
>    perf tools: Add RISC-V trace PMU record capabilities
>    perf tools: Add Nexus RISC-V Trace decoder
>    perf symbols: Add RISC-V PLT entry sizes
>    perf tools: Integrate RISC-V trace decoder into auxtrace
> 
>   drivers/hwtracing/Kconfig                     |    2 +
>   drivers/hwtracing/coresight/Kconfig           |   46 +-
>   drivers/hwtracing/coresight/Makefile          |    6 +
>   drivers/hwtracing/coresight/coresight-core.c  |    8 +
>   .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
>   .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
>   .../hwtracing/coresight/coresight-platform.c  |    1 -
>   .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
>   .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
>   .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
>   drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
>   .../coresight/rvtrace-encoder-core.c          |  562 +++++++
>   .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
>   drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
>   drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
>   drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
>   .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
>   .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
>   include/linux/coresight-pmu.h                 |    4 +
>   include/linux/rvtrace.h                       |  116 ++
>   tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
>   tools/perf/arch/riscv/util/Build              |    2 +
>   tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
>   tools/perf/arch/riscv/util/pmu.c              |   20 +
>   tools/perf/util/Build                         |    3 +
>   tools/perf/util/auxtrace.c                    |    4 +
>   tools/perf/util/auxtrace.h                    |    1 +
>   tools/perf/util/nexus-rv-decoder/Build        |    1 +
>   .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
>   .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
>   .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
>   tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
>   tools/perf/util/rvtrace.h                     |   40 +
>   tools/perf/util/symbol-elf.c                  |    4 +
>   34 files changed, 6320 insertions(+), 3 deletions(-)
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
>   create mode 100644 include/linux/rvtrace.h
>   create mode 100644 tools/arch/riscv/include/asm/insn.h
>   create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>   create mode 100644 tools/perf/arch/riscv/util/pmu.c
>   create mode 100644 tools/perf/util/nexus-rv-decoder/Build
>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
>   create mode 100644 tools/perf/util/rvtrace-decoder.c
>   create mode 100644 tools/perf/util/rvtrace.h
> 


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
                   ` (12 preceding siblings ...)
  2026-04-14  4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
@ 2026-04-14  7:23 ` Anup Patel
  2026-04-14  9:04   ` Zane Leung
  13 siblings, 1 reply; 20+ messages in thread
From: Anup Patel @ 2026-04-14  7:23 UTC (permalink / raw)
  To: Zane Leung
  Cc: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers, coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, atish.patra, andrew.jones,
	sunilvl, linux-riscv, linux-kernel, anup.patel, mayuresh.chitale,
	zhuangqiubin

On Tue, Apr 14, 2026 at 9:12 AM Zane Leung <liangzhen@linux.spacemit.com> wrote:
>
> From: liangzhen <zhen.liang@spacemit.com>
>
> This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
> trace drivers within the CoreSight framework and integrating them with perf tools.
> The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
> components, which can be directly leveraged through the existing CoreSight infrastructure.
>
> Linux RISC-V trace support form Anup Patel:
> (https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
> which currently lacks ATB component support and guidance on reusing CoreSight components.

What stops you from adding RISC-V trace funnel and ATB bridge drivers
on top of this series ?

>
> The series includes:
> - RISC-V trace driver implementation within the CoreSight framework

The RISC-V trace very different from the CoreSight framework in many ways:
1) Types of components supported
2) Trace packet formats
3) The way MMIO based components are discoverd
4) ... and more ...

> - RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
> - RISC-V trace PMU record capabilities and parsing events in perf.
> - RISC-V Nexus Trace decoder for perf tools
>
> Any comments or suggestions are welcome.
>
> Verification on K3 SoC:
> To verify this patch series on K3 hardware, the following device tree are required:
> 1. RISC-V Trace Encoder node (8)
> 2. RISC-V ATB Bridge node (8)
> 3. RISC-V Trace Funnel node (2)
> 3. CoreSight Funnel configuration for RISC-V (1)
> 4. CoreSight TMC configuration for trace buffer (1)
>
> /{
>         dummy_clk: apb-pclk {
>                 compatible = "fixed-clock";
>                 #clock-cells = <0x0>;
>                 clock-output-names = "clk14mhz";
>                 clock-frequency = <14000000>;
>         };
>
>
>         soc: soc {
>                 #address-cells = <2>;
>                 #size-cells = <2>;
>
>                 encoder0: encoder@d9002000 {
>                         compatible = "riscv,trace-encoder";
>                         reg = <0x0 0xd9002000 0x0 0x1000>;
>                         cpus = <&cpu_0>;
>                         out-ports {
>                                 port {
>                                         cluster0_encoder0_out_port: endpoint {
>                                                 remote-endpoint = <&cluster0_bridge0_in_port>;
>                                         };
>                                 };
>                         };
>                 };
>
>                 bridge0: bridge@d9003000 {
>                         compatible = "riscv,trace-atbbridge";
>                         reg = <0x0 0xd9003000 0x0 0x1000>;
>                         cpus = <&cpu_0>;
>                         out-ports {
>                                 port {
>                                         cluster0_bridge0_out_port: endpoint {
>                                                 remote-endpoint = <&cluster0_funnel_in_port0>;
>                                         };
>                                 };
>                         };
>                         in-ports {
>                                 port {
>                                         cluster0_bridge0_in_port: endpoint {
>                                                 remote-endpoint = <&cluster0_encoder0_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>
> ...
>
>                 cluster0_funnel: funnel@d9000000 {
>                         compatible = "riscv,trace-funnel";
>                         reg = <0x0 0xd9000000 0x0 0x1000>;
>                         cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
>                         riscv,timestamp-present;
>                         out-ports {
>                                 port {
>                                         cluster0_funnel_out_port: endpoint {
>                                                 remote-endpoint = <&main_funnel_in_port0>;
>                                         };
>                                 };
>                         };
>                         in-ports {
>                                 #address-cells = <1>;
>                                 #size-cells = <0>;
>
>                                 port@0 {
>                                         reg = <0>;
>                                         cluster0_funnel_in_port0: endpoint {
>                                                 remote-endpoint = <&cluster0_bridge0_out_port>;
>                                         };
>                                 };
>
>                                 port@1 {
>                                         reg = <1>;
>                                         cluster0_funnel_in_port1: endpoint {
>                                                 remote-endpoint = <&cluster0_bridge1_out_port>;
>                                         };
>                                 };
>
>                                 port@2 {
>                                         reg = <2>;
>                                         cluster0_funnel_in_port2: endpoint {
>                                                 remote-endpoint = <&cluster0_bridge2_out_port>;
>                                         };
>                                 };
>
>                                 port@3 {
>                                         reg = <3>;
>                                         cluster0_funnel_in_port3: endpoint {
>                                                 remote-endpoint = <&cluster0_bridge3_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>
>                 cluster1_funnel: funnel@d9010000 {
>                         compatible = "riscv,trace-funnel";
>                         reg = <0x0 0xd9010000 0x0 0x1000>;
>                         cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
>                         riscv,timestamp-present;
>                         out-ports {
>                                 port {
>                                         cluster1_funnel_out_port: endpoint {
>                                                 remote-endpoint = <&main_funnel_in_port1>;
>                                         };
>                                 };
>                         };
>                         in-ports {
>                                 #address-cells = <1>;
>                                 #size-cells = <0>;
>
>                                 port@0 {
>                                         reg = <0>;
>                                         cluster1_funnel_in_port0: endpoint {
>                                                 remote-endpoint = <&cluster1_bridge0_out_port>;
>                                         };
>                                 };
>
>                                 port@1 {
>                                         reg = <1>;
>                                         cluster1_funnel_in_port1: endpoint {
>                                                 remote-endpoint = <&cluster1_bridge1_out_port>;
>                                         };
>                                 };
>
>                                 port@2 {
>                                         reg = <2>;
>                                         cluster1_funnel_in_port2: endpoint {
>                                                 remote-endpoint = <&cluster1_bridge2_out_port>;
>                                         };
>                                 };
>
>                                 port@3 {
>                                         reg = <3>;
>                                         cluster1_funnel_in_port3: endpoint {
>                                                 remote-endpoint = <&cluster1_bridge3_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>
>                 main_funnel: funnel@d9042000 {
>                         compatible = "arm,coresight-dynamic-funnel", "arm,primecell";

Is it legally allowed to mix and match ARM coresight IPs with
RISC-V trace components at hardware level ?

>                         reg = <0x0 0xd9042000 0x0 0x1000>;
>                         clocks = <&dummy_clk>;
>                         clock-names = "apb_pclk";
>                         out-ports {
>                                 port {
>                                         main_funnel_out_port: endpoint {
>                                                 remote-endpoint = <&etf_in_port>;
>                                         };
>                                 };
>                         };
>                         in-ports {
>                                 #address-cells = <1>;
>                                 #size-cells = <0>;
>
>                                 port@0 {
>                                         reg = <0>;
>                                         main_funnel_in_port0: endpoint {
>                                                 remote-endpoint = <&cluster0_funnel_out_port>;
>                                         };
>                                 };
>
>                                 port@1 {
>                                         reg = <1>;
>                                         main_funnel_in_port1: endpoint {
>                                                 remote-endpoint = <&cluster1_funnel_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>
>                 etf: etf@d9043000 {
>                         compatible = "arm,coresight-tmc", "arm,primecell";
>                         reg = <0x0 0xd9043000 0x0 0x1000>;
>                         clocks = <&dummy_clk>;
>                         clock-names = "apb_pclk";
>                         out-ports {
>                                 port {
>                                         etf_out_port: endpoint {
>                                                 remote-endpoint = <&etr_in_port>;
>                                         };
>                                 };
>                         };
>                         in-ports {
>                                 port {
>                                         etf_in_port: endpoint {
>                                                 remote-endpoint = <&main_funnel_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>
>                 etr: etr@d9044000 {
>                         compatible = "arm,coresight-tmc", "arm,primecell";
>                         reg = <0x0 0xd9044000 0x0 0x1000>;
>                         clocks = <&dummy_clk>;
>                         clock-names = "apb_pclk";
>                         arm,scatter-gather;
>                         in-ports {
>                                 port {
>                                         etr_in_port: endpoint {
>                                                 remote-endpoint = <&etf_out_port>;
>                                         };
>                                 };
>                         };
>                 };
>         };
> };
>
> Verification case:
>
> ~ # perf list pmu
>   rvtrace//                                          [Kernel PMU event]
>
> ~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
> Linux
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.191 MB perf.data ]
> ~ # perf script
>            uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
>            uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
> ...
>
> liangzhen (12):
>   coresight: Add RISC-V support to CoreSight tracing
>   coresight: Initial implementation of RISC-V trace driver
>   coresight: Add RISC-V Trace Encoder driver
>   coresight: Add RISC-V Trace Funnel driver
>   coresight: Add RISC-V Trace ATB Bridge driver
>   coresight rvtrace: Add timestamp component support for encoder and
>     funnel
>   coresight: Add RISC-V PMU name support
>   perf tools: riscv: making rvtrace PMU listable
>   perf tools: Add RISC-V trace PMU record capabilities
>   perf tools: Add Nexus RISC-V Trace decoder
>   perf symbols: Add RISC-V PLT entry sizes
>   perf tools: Integrate RISC-V trace decoder into auxtrace
>
>  drivers/hwtracing/Kconfig                     |    2 +
>  drivers/hwtracing/coresight/Kconfig           |   46 +-
>  drivers/hwtracing/coresight/Makefile          |    6 +
>  drivers/hwtracing/coresight/coresight-core.c  |    8 +
>  .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
>  .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
>  .../hwtracing/coresight/coresight-platform.c  |    1 -
>  .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
>  .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
>  .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
>  drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
>  .../coresight/rvtrace-encoder-core.c          |  562 +++++++
>  .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
>  drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
>  drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
>  drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
>  .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
>  .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
>  include/linux/coresight-pmu.h                 |    4 +
>  include/linux/rvtrace.h                       |  116 ++
>  tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
>  tools/perf/arch/riscv/util/Build              |    2 +
>  tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
>  tools/perf/arch/riscv/util/pmu.c              |   20 +
>  tools/perf/util/Build                         |    3 +
>  tools/perf/util/auxtrace.c                    |    4 +
>  tools/perf/util/auxtrace.h                    |    1 +
>  tools/perf/util/nexus-rv-decoder/Build        |    1 +
>  .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
>  .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
>  .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
>  tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
>  tools/perf/util/rvtrace.h                     |   40 +
>  tools/perf/util/symbol-elf.c                  |    4 +
>  34 files changed, 6320 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
>  create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
>  create mode 100644 include/linux/rvtrace.h
>  create mode 100644 tools/arch/riscv/include/asm/insn.h
>  create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>  create mode 100644 tools/perf/arch/riscv/util/pmu.c
>  create mode 100644 tools/perf/util/nexus-rv-decoder/Build
>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
>  create mode 100644 tools/perf/util/rvtrace-decoder.c
>  create mode 100644 tools/perf/util/rvtrace.h
>
> --
> 2.34.1
>

NACK to this approach of retrofitting RISC-V trace into ARM coresight.

Regards,
Anup

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-14  4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
@ 2026-04-14  8:08   ` Zane Leung
  0 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-14  8:08 UTC (permalink / raw)
  To: Jie Gan, robh, krzk+dt, conor+dt, palmer, pjw, gregkh,
	alexander.shishkin, irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin


On 4/14/2026 12:15 PM, Jie Gan wrote:
>
> I dont think you have looped relevant maintainers and reviewers for the Coresight subsystem.
>
> Thanks,
> Jie 

I will send it to all relevant maintainers and reviewers for the Coresight subsystem in next revision.

Thanks,
Zane

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-14  7:23 ` Anup Patel
@ 2026-04-14  9:04   ` Zane Leung
  2026-04-15  0:10     ` Bo Gan
  0 siblings, 1 reply; 20+ messages in thread
From: Zane Leung @ 2026-04-14  9:04 UTC (permalink / raw)
  To: Anup Patel
  Cc: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers, coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, mchitale, atish.patra, andrew.jones, sunilvl,
	linux-riscv, linux-kernel, anup.patel, mayuresh.chitale,
	zhuangqiubin, suzuki.poulose, mike.leach, james.clark,
	alexander.shishkin, linux-arm-kernel


On 4/14/2026 3:23 PM, Anup Patel wrote:
> On Tue, Apr 14, 2026 at 9:12 AM Zane Leung <liangzhen@linux.spacemit.com> wrote:
>> From: liangzhen <zhen.liang@spacemit.com>
>>
>> This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
>> trace drivers within the CoreSight framework and integrating them with perf tools.
>> The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
>> components, which can be directly leveraged through the existing CoreSight infrastructure.
>>
>> Linux RISC-V trace support form Anup Patel:
>> (https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
>> which currently lacks ATB component support and guidance on reusing CoreSight components.
> What stops you from adding RISC-V trace funnel and ATB bridge drivers
> on top of this series ?
>
Firstly, my works started much earlier than this series. Secondly, it is difficult to add the coresight funnel and coresight tmc components to this series. Based on the coresight framework, I can directly reuse them.

>
>> The series includes:
>> - RISC-V trace driver implementation within the CoreSight framework
> The RISC-V trace very different from the CoreSight framework in many ways:
> 1) Types of components supported
> 2) Trace packet formats
> 3) The way MMIO based components are discoverd
> 4) ... and more ...
1) I believe that RISC-V tracing is coresight-alike, where have encoders/funnel/sink/bridge that are described through DT and controlled by MMIO. 
2) I think the difference in package format is nothing more than the coresight frame generated by the ATB component to distinguish the trace source. After removing it, it becomes riscv trace data.
3) The current CoreSight framework code does not introduce this mechanism, it is described through DT.
>> - RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
>> - RISC-V trace PMU record capabilities and parsing events in perf.
>> - RISC-V Nexus Trace decoder for perf tools
>>
>> Any comments or suggestions are welcome.
>>
>> Verification on K3 SoC:
>> To verify this patch series on K3 hardware, the following device tree are required:
>> 1. RISC-V Trace Encoder node (8)
>> 2. RISC-V ATB Bridge node (8)
>> 3. RISC-V Trace Funnel node (2)
>> 3. CoreSight Funnel configuration for RISC-V (1)
>> 4. CoreSight TMC configuration for trace buffer (1)
>>
>> /{
>>         dummy_clk: apb-pclk {
>>                 compatible = "fixed-clock";
>>                 #clock-cells = <0x0>;
>>                 clock-output-names = "clk14mhz";
>>                 clock-frequency = <14000000>;
>>         };
>>
>>
>>         soc: soc {
>>                 #address-cells = <2>;
>>                 #size-cells = <2>;
>>
>>                 encoder0: encoder@d9002000 {
>>                         compatible = "riscv,trace-encoder";
>>                         reg = <0x0 0xd9002000 0x0 0x1000>;
>>                         cpus = <&cpu_0>;
>>                         out-ports {
>>                                 port {
>>                                         cluster0_encoder0_out_port: endpoint {
>>                                                 remote-endpoint = <&cluster0_bridge0_in_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>>                 bridge0: bridge@d9003000 {
>>                         compatible = "riscv,trace-atbbridge";
>>                         reg = <0x0 0xd9003000 0x0 0x1000>;
>>                         cpus = <&cpu_0>;
>>                         out-ports {
>>                                 port {
>>                                         cluster0_bridge0_out_port: endpoint {
>>                                                 remote-endpoint = <&cluster0_funnel_in_port0>;
>>                                         };
>>                                 };
>>                         };
>>                         in-ports {
>>                                 port {
>>                                         cluster0_bridge0_in_port: endpoint {
>>                                                 remote-endpoint = <&cluster0_encoder0_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>> ...
>>
>>                 cluster0_funnel: funnel@d9000000 {
>>                         compatible = "riscv,trace-funnel";
>>                         reg = <0x0 0xd9000000 0x0 0x1000>;
>>                         cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
>>                         riscv,timestamp-present;
>>                         out-ports {
>>                                 port {
>>                                         cluster0_funnel_out_port: endpoint {
>>                                                 remote-endpoint = <&main_funnel_in_port0>;
>>                                         };
>>                                 };
>>                         };
>>                         in-ports {
>>                                 #address-cells = <1>;
>>                                 #size-cells = <0>;
>>
>>                                 port@0 {
>>                                         reg = <0>;
>>                                         cluster0_funnel_in_port0: endpoint {
>>                                                 remote-endpoint = <&cluster0_bridge0_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@1 {
>>                                         reg = <1>;
>>                                         cluster0_funnel_in_port1: endpoint {
>>                                                 remote-endpoint = <&cluster0_bridge1_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@2 {
>>                                         reg = <2>;
>>                                         cluster0_funnel_in_port2: endpoint {
>>                                                 remote-endpoint = <&cluster0_bridge2_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@3 {
>>                                         reg = <3>;
>>                                         cluster0_funnel_in_port3: endpoint {
>>                                                 remote-endpoint = <&cluster0_bridge3_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>>                 cluster1_funnel: funnel@d9010000 {
>>                         compatible = "riscv,trace-funnel";
>>                         reg = <0x0 0xd9010000 0x0 0x1000>;
>>                         cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
>>                         riscv,timestamp-present;
>>                         out-ports {
>>                                 port {
>>                                         cluster1_funnel_out_port: endpoint {
>>                                                 remote-endpoint = <&main_funnel_in_port1>;
>>                                         };
>>                                 };
>>                         };
>>                         in-ports {
>>                                 #address-cells = <1>;
>>                                 #size-cells = <0>;
>>
>>                                 port@0 {
>>                                         reg = <0>;
>>                                         cluster1_funnel_in_port0: endpoint {
>>                                                 remote-endpoint = <&cluster1_bridge0_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@1 {
>>                                         reg = <1>;
>>                                         cluster1_funnel_in_port1: endpoint {
>>                                                 remote-endpoint = <&cluster1_bridge1_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@2 {
>>                                         reg = <2>;
>>                                         cluster1_funnel_in_port2: endpoint {
>>                                                 remote-endpoint = <&cluster1_bridge2_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@3 {
>>                                         reg = <3>;
>>                                         cluster1_funnel_in_port3: endpoint {
>>                                                 remote-endpoint = <&cluster1_bridge3_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>>                 main_funnel: funnel@d9042000 {
>>                         compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> Is it legally allowed to mix and match ARM coresight IPs with
> RISC-V trace components at hardware level ?
The ATB Bridge allows sending RISC-V trace to Arm CoreSight infrastructure (instead of RISC-V compliant sink defined in this document) as an ATB initiator.  see: 
https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L184
For ATB Bridge, read trace using Coresight components (ETB/TMC/TPIU). see:
https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L1684
>
>>                         reg = <0x0 0xd9042000 0x0 0x1000>;
>>                         clocks = <&dummy_clk>;
>>                         clock-names = "apb_pclk";
>>                         out-ports {
>>                                 port {
>>                                         main_funnel_out_port: endpoint {
>>                                                 remote-endpoint = <&etf_in_port>;
>>                                         };
>>                                 };
>>                         };
>>                         in-ports {
>>                                 #address-cells = <1>;
>>                                 #size-cells = <0>;
>>
>>                                 port@0 {
>>                                         reg = <0>;
>>                                         main_funnel_in_port0: endpoint {
>>                                                 remote-endpoint = <&cluster0_funnel_out_port>;
>>                                         };
>>                                 };
>>
>>                                 port@1 {
>>                                         reg = <1>;
>>                                         main_funnel_in_port1: endpoint {
>>                                                 remote-endpoint = <&cluster1_funnel_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>>                 etf: etf@d9043000 {
>>                         compatible = "arm,coresight-tmc", "arm,primecell";
>>                         reg = <0x0 0xd9043000 0x0 0x1000>;
>>                         clocks = <&dummy_clk>;
>>                         clock-names = "apb_pclk";
>>                         out-ports {
>>                                 port {
>>                                         etf_out_port: endpoint {
>>                                                 remote-endpoint = <&etr_in_port>;
>>                                         };
>>                                 };
>>                         };
>>                         in-ports {
>>                                 port {
>>                                         etf_in_port: endpoint {
>>                                                 remote-endpoint = <&main_funnel_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>
>>                 etr: etr@d9044000 {
>>                         compatible = "arm,coresight-tmc", "arm,primecell";
>>                         reg = <0x0 0xd9044000 0x0 0x1000>;
>>                         clocks = <&dummy_clk>;
>>                         clock-names = "apb_pclk";
>>                         arm,scatter-gather;
>>                         in-ports {
>>                                 port {
>>                                         etr_in_port: endpoint {
>>                                                 remote-endpoint = <&etf_out_port>;
>>                                         };
>>                                 };
>>                         };
>>                 };
>>         };
>> };
>>
>> Verification case:
>>
>> ~ # perf list pmu
>>   rvtrace//                                          [Kernel PMU event]
>>
>> ~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
>> Linux
>> [ perf record: Woken up 1 times to write data ]
>> [ perf record: Captured and wrote 0.191 MB perf.data ]
>> ~ # perf script
>>            uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
>>            uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
>> ...
>>
>> liangzhen (12):
>>   coresight: Add RISC-V support to CoreSight tracing
>>   coresight: Initial implementation of RISC-V trace driver
>>   coresight: Add RISC-V Trace Encoder driver
>>   coresight: Add RISC-V Trace Funnel driver
>>   coresight: Add RISC-V Trace ATB Bridge driver
>>   coresight rvtrace: Add timestamp component support for encoder and
>>     funnel
>>   coresight: Add RISC-V PMU name support
>>   perf tools: riscv: making rvtrace PMU listable
>>   perf tools: Add RISC-V trace PMU record capabilities
>>   perf tools: Add Nexus RISC-V Trace decoder
>>   perf symbols: Add RISC-V PLT entry sizes
>>   perf tools: Integrate RISC-V trace decoder into auxtrace
>>
>>  drivers/hwtracing/Kconfig                     |    2 +
>>  drivers/hwtracing/coresight/Kconfig           |   46 +-
>>  drivers/hwtracing/coresight/Makefile          |    6 +
>>  drivers/hwtracing/coresight/coresight-core.c  |    8 +
>>  .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
>>  .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
>>  .../hwtracing/coresight/coresight-platform.c  |    1 -
>>  .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
>>  .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
>>  .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
>>  drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
>>  .../coresight/rvtrace-encoder-core.c          |  562 +++++++
>>  .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
>>  drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
>>  drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
>>  drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
>>  .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
>>  .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
>>  include/linux/coresight-pmu.h                 |    4 +
>>  include/linux/rvtrace.h                       |  116 ++
>>  tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
>>  tools/perf/arch/riscv/util/Build              |    2 +
>>  tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
>>  tools/perf/arch/riscv/util/pmu.c              |   20 +
>>  tools/perf/util/Build                         |    3 +
>>  tools/perf/util/auxtrace.c                    |    4 +
>>  tools/perf/util/auxtrace.h                    |    1 +
>>  tools/perf/util/nexus-rv-decoder/Build        |    1 +
>>  .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
>>  .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
>>  .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
>>  tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
>>  tools/perf/util/rvtrace.h                     |   40 +
>>  tools/perf/util/symbol-elf.c                  |    4 +
>>  34 files changed, 6320 insertions(+), 3 deletions(-)
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
>>  create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
>>  create mode 100644 include/linux/rvtrace.h
>>  create mode 100644 tools/arch/riscv/include/asm/insn.h
>>  create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>>  create mode 100644 tools/perf/arch/riscv/util/pmu.c
>>  create mode 100644 tools/perf/util/nexus-rv-decoder/Build
>>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
>>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
>>  create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
>>  create mode 100644 tools/perf/util/rvtrace-decoder.c
>>  create mode 100644 tools/perf/util/rvtrace.h
>>
>> --
>> 2.34.1
>>
> NACK to this approach of retrofitting RISC-V trace into ARM coresight.
I agree that integrating RISC-V trace directly into CoreSight is not a good approach, so I think we should abstract some of the logic of coresight and reuse it in RISC-V Trace.
>
> Regards,
> Anup

Thanks,
Zane

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities
  2026-04-14  3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
@ 2026-04-14 23:31   ` Bo Gan
  0 siblings, 0 replies; 20+ messages in thread
From: Bo Gan @ 2026-04-14 23:31 UTC (permalink / raw)
  To: Zane Leung, robh, krzk+dt, conor+dt, palmer, pjw, gregkh,
	alexander.shishkin, irogers
  Cc: coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, kan.liang, mchitale, anup, atish.patra,
	andrew.jones, sunilvl, linux-riscv, linux-kernel, anup.patel,
	mayuresh.chitale, zhuangqiubin

NACK. The file is largely copied from tools/perf/arch/x86/util/intel-pt.c
There's even markers like "Intel Processor Trace" in file auxtrace.c.
There's no mentioning of authorship, but instead Spacemit claming its
copyright over it.

Bo

On 4/13/26 20:41, Zane Leung wrote:
> From: liangzhen <zhen.liang@spacemit.com>
> 
> Introduce the required auxiliary API functions allowing
> the perf core to interact with RISC-V trace perf driver.
> 
> Signed-off-by: liangzhen <zhen.liang@spacemit.com>
> ---
>   tools/perf/arch/riscv/util/Build      |   2 +-
>   tools/perf/arch/riscv/util/auxtrace.c | 489 ++++++++++++++++++++++++++
>   tools/perf/util/auxtrace.c            |   1 +
>   tools/perf/util/auxtrace.h            |   1 +
>   tools/perf/util/rvtrace.h             |  38 ++
>   5 files changed, 530 insertions(+), 1 deletion(-)
>   create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>   create mode 100644 tools/perf/util/rvtrace.h
> 
> diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
> index 748068a3a5c5..10f41f97bc2e 100644
> --- a/tools/perf/arch/riscv/util/Build
> +++ b/tools/perf/arch/riscv/util/Build
> @@ -1,3 +1,3 @@
>   perf-util-y += header.o
>   
> -perf-util-y += pmu.o
> +perf-util-y += pmu.o auxtrace.o
> diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
> new file mode 100644
> index 000000000000..5feee198ef97
> --- /dev/null
> +++ b/tools/perf/arch/riscv/util/auxtrace.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(C) 2026 Spacemit Limited. All rights reserved.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/bitops.h>
> +#include <linux/log2.h>
> +#include <linux/zalloc.h>
> +#include <linux/string.h>
> +#include <time.h>
> +
> +#include <internal/lib.h> // page_size
> +#include "../../../util/auxtrace.h"
> +#include "../../../util/cpumap.h"
> +#include "../../../util/debug.h"
> +#include "../../../util/event.h"
> +#include "../../../util/evlist.h"
> +#include "../../../util/evsel.h"
> +#include "../../../util/rvtrace.h"
> +#include "../../../util/pmu.h"
> +#include "../../../util/record.h"
> +#include "../../../util/session.h"
> +#include "../../../util/tsc.h"
> +#include "../../../util/evsel_config.h"
> +
> +#define RVTRACE_PMU_NAME "rvtrace"
> +#define KiB(x) ((x) * 1024)
> +#define MiB(x) ((x) * 1024 * 1024)
> +
> +static const char * const metadata_encoder_ro[] = {
> +	[RVTRACE_ENCODER_FORMAT]    = "control/format",
> +	[RVTRACE_ENCODER_CONTEXT]   = "control/context",
> +	[RVTRACE_ENCODER_INHB_SRC]  = "control/inhb_src",
> +	[RVTRACE_ENCODER_SRCBITS]   = "features/srcb",
> +	[RVTRACE_ENCODER_SRCID]	    = "features/srcid"
> +};
> +
> +struct rvtrace_recording {
> +	struct auxtrace_record	itr;
> +	struct perf_pmu *rvtrace_pmu;
> +	struct evlist *evlist;
> +	bool snapshot_mode;
> +	size_t snapshot_size;
> +};
> +
> +static int rvtrace_parse_snapshot_options(struct auxtrace_record *itr,
> +					  struct record_opts *opts,
> +					  const char *str)
> +{
> +	struct rvtrace_recording *ptr =
> +				container_of(itr, struct rvtrace_recording, itr);
> +	unsigned long long snapshot_size = 0;
> +	char *endptr;
> +
> +	if (str) {
> +		snapshot_size = strtoull(str, &endptr, 0);
> +		if (*endptr || snapshot_size > SIZE_MAX)
> +			return -1;
> +	}
> +
> +	opts->auxtrace_snapshot_mode = true;
> +	opts->auxtrace_snapshot_size = snapshot_size;
> +	ptr->snapshot_size = snapshot_size;
> +
> +	return 0;
> +}
> +
> +static size_t rvtrace_info_priv_size(struct auxtrace_record *itr __maybe_unused,
> +				     struct evlist *evlist __maybe_unused)
> +{
> +	int encoder;
> +	struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
> +	struct perf_cpu_map *intersect_cpus;
> +
> +	if (!perf_cpu_map__has_any_cpu(event_cpus)) {
> +		/* cpu map is not "any" CPU , we have specific CPUs to work with */
> +		struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
> +
> +		intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
> +		perf_cpu_map__put(online_cpus);
> +	} else {
> +		/* Event can be "any" CPU so count all online CPUs. */
> +		intersect_cpus = perf_cpu_map__new_online_cpus();
> +	}
> +
> +	encoder = perf_cpu_map__nr(intersect_cpus);
> +	perf_cpu_map__put(intersect_cpus);
> +
> +	return (RVTRACE_HEADER_SIZE + encoder * RVTRACE_ENCODER_PRIV_SIZE);
> +}
> +
> +static int rvtrace_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val)
> +{
> +	char pmu_path[PATH_MAX];
> +	int scan;
> +
> +	/* Get RO metadata from sysfs */
> +	snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
> +
> +	scan = perf_pmu__scan_file(pmu, pmu_path, "%llx", val);
> +	if (scan != 1) {
> +		pr_err("%s: error reading: %s\n", __func__, pmu_path);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void rvtrace_get_metadata(struct perf_cpu cpu, u32 *offset,
> +				 struct auxtrace_record *itr,
> +				 struct perf_record_auxtrace_info *info)
> +{
> +	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
> +	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
> +
> +	info->priv[*offset + RVTRACE_ENCODER_CPU] = cpu.cpu;
> +	info->priv[*offset + RVTRACE_ENCODER_NR_TRC_PARAMS] = RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH;
> +
> +	/* Get read-only information from sysFS */
> +	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_FORMAT],
> +			&info->priv[*offset + RVTRACE_ENCODER_FORMAT]);
> +	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT],
> +			&info->priv[*offset + RVTRACE_ENCODER_CONTEXT]);
> +	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_INHB_SRC],
> +			&info->priv[*offset + RVTRACE_ENCODER_INHB_SRC]);
> +	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCBITS],
> +			&info->priv[*offset + RVTRACE_ENCODER_SRCBITS]);
> +	rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCID],
> +			&info->priv[*offset + RVTRACE_ENCODER_SRCID]);
> +
> +	/* Where the next CPU entry should start from */
> +	*offset += RVTRACE_ENCODER_PRIV_MAX;
> +}
> +
> +static int rvtrace_info_fill(struct auxtrace_record *itr, struct perf_session *session,
> +			     struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size)
> +{
> +	int i;
> +	u32 offset;
> +	u64 nr_cpu, type;
> +	struct perf_cpu_map *cpu_map;
> +	struct perf_cpu_map *event_cpus = session->evlist->core.user_requested_cpus;
> +	struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
> +	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
> +	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
> +	struct perf_cpu cpu;
> +
> +	if (priv_size != rvtrace_info_priv_size(itr, session->evlist))
> +		return -EINVAL;
> +
> +	if (!session->evlist->core.nr_mmaps)
> +		return -EINVAL;
> +
> +	/* If the cpu_map has the "any" CPU all online CPUs are involved */
> +	if (perf_cpu_map__has_any_cpu(event_cpus)) {
> +		cpu_map = online_cpus;
> +	} else {
> +		/* Make sure all specified CPUs are online */
> +		perf_cpu_map__for_each_cpu(cpu, i, event_cpus) {
> +			if (!perf_cpu_map__has(online_cpus, cpu))
> +				return -EINVAL;
> +		}
> +
> +		cpu_map = event_cpus;
> +	}
> +
> +	nr_cpu = perf_cpu_map__nr(cpu_map);
> +	type = rvtrace_pmu->type;
> +
> +	/* First fill out the session header */
> +	auxtrace_info->type = PERF_AUXTRACE_RISCV_TRACE;
> +	auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] = type << 32;
> +	auxtrace_info->priv[RVTRACE_PMU_TYPE_CPUS] |= nr_cpu;
> +
> +	offset = RVTRACE_HEADER_MAX;
> +
> +	perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
> +		assert(offset < priv_size);
> +		rvtrace_get_metadata(cpu, &offset, itr, auxtrace_info);
> +	}
> +
> +	perf_cpu_map__put(online_cpus);
> +
> +	return 0;
> +}
> +
> +static int rvtrace_set_sink_attr(struct perf_pmu *pmu,
> +				 struct evsel *evsel)
> +{
> +	char msg[BUFSIZ], path[PATH_MAX], *sink;
> +	struct evsel_config_term *term;
> +	int ret = -EINVAL;
> +	u32 hash;
> +
> +	if (evsel->core.attr.config2 & GENMASK(31, 0))
> +		return 0;
> +
> +	list_for_each_entry(term, &evsel->config_terms, list) {
> +		if (term->type != EVSEL__CONFIG_TERM_DRV_CFG)
> +			continue;
> +
> +		sink = term->val.str;
> +		snprintf(path, PATH_MAX, "sinks/%s", sink);
> +
> +		ret = perf_pmu__scan_file(pmu, path, "%x", &hash);
> +		if (ret != 1) {
> +			if (errno == ENOENT)
> +				pr_err("Couldn't find sink \"%s\" on event %s\n"
> +				       "Missing kernel or device support?\n\n"
> +				       "Hint: An appropriate sink will be picked automatically if one isn't specified.\n",
> +				       sink, evsel__name(evsel));
> +			else
> +				pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n",
> +				       sink, evsel__name(evsel), errno,
> +				       str_error_r(errno, msg, sizeof(msg)));
> +			return ret;
> +		}
> +
> +		evsel->core.attr.config2 |= hash;
> +		return 0;
> +	}
> +
> +	/*
> +	 * No sink was provided on the command line - allow the CoreSight
> +	 * system to look for a default
> +	 */
> +	return 0;
> +}
> +
> +static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist *evlist,
> +				     struct record_opts *opts)
> +{
> +	struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
> +	struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
> +	struct evsel *evsel, *rvtrace_evsel = NULL;
> +	struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
> +	bool privileged = perf_event_paranoid_check(-1);
> +	struct evsel *tracking_evsel;
> +	int err;
> +
> +	ptr->evlist = evlist;
> +	ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
> +	evlist__for_each_entry(evlist, evsel) {
> +		if (evsel->core.attr.type == rvtrace_pmu->type) {
> +			if (rvtrace_evsel) {
> +				pr_err("There may be only one " RVTRACE_PMU_NAME "x event\n");
> +				return -EINVAL;
> +			}
> +			evsel->core.attr.freq = 0;
> +			evsel->core.attr.sample_period = 1;
> +			evsel->needs_auxtrace_mmap = true;
> +			rvtrace_evsel = evsel;
> +			opts->full_auxtrace = true;
> +		}
> +	}
> +
> +	if (!opts->full_auxtrace)
> +		return 0;
> +
> +	err = rvtrace_set_sink_attr(rvtrace_pmu, rvtrace_evsel);
> +	if (err)
> +		return err;
> +
> +	/* we are in snapshot mode */
> +	if (opts->auxtrace_snapshot_mode) {
> +		/*
> +		 * No size were given to '-S' or '-m,', so go with
> +		 * the default
> +		 */
> +		if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
> +			if (privileged) {
> +				opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +			} else {
> +				opts->auxtrace_mmap_pages = KiB(128) / page_size;
> +				if (opts->mmap_pages == UINT_MAX)
> +					opts->mmap_pages = KiB(256) / page_size;
> +			}
> +		} else if (!opts->auxtrace_mmap_pages && !privileged &&
> +						opts->mmap_pages == UINT_MAX) {
> +			opts->mmap_pages = KiB(256) / page_size;
> +		}
> +
> +		/*
> +		 * '-m,xyz' was specified but no snapshot size, so make the
> +		 * snapshot size as big as the auxtrace mmap area.
> +		 */
> +		if (!opts->auxtrace_snapshot_size) {
> +			opts->auxtrace_snapshot_size =
> +				opts->auxtrace_mmap_pages * (size_t)page_size;
> +		}
> +
> +		/*
> +		 * -Sxyz was specified but no auxtrace mmap area, so make the
> +		 * auxtrace mmap area big enough to fit the requested snapshot
> +		 * size.
> +		 */
> +		if (!opts->auxtrace_mmap_pages) {
> +			size_t sz = opts->auxtrace_snapshot_size;
> +
> +			sz = round_up(sz, page_size) / page_size;
> +			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
> +		}
> +
> +		/* Snapshot size can't be bigger than the auxtrace area */
> +		if (opts->auxtrace_snapshot_size >
> +				opts->auxtrace_mmap_pages * (size_t)page_size) {
> +			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
> +			       opts->auxtrace_snapshot_size,
> +			       opts->auxtrace_mmap_pages * (size_t)page_size);
> +			return -EINVAL;
> +		}
> +
> +		/* Something went wrong somewhere - this shouldn't happen */
> +		if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
> +			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
> +			return -EINVAL;
> +		}
> +
> +		pr_debug2("%s snapshot size: %zu\n", RVTRACE_PMU_NAME,
> +			  opts->auxtrace_snapshot_size);
> +	}
> +
> +	/* Buffer sizes weren't specified with '-m,xyz' so give some defaults */
> +	if (!opts->auxtrace_mmap_pages) {
> +		if (privileged) {
> +			opts->auxtrace_mmap_pages = MiB(4) / page_size;
> +		} else {
> +			opts->auxtrace_mmap_pages = KiB(128) / page_size;
> +			if (opts->mmap_pages == UINT_MAX)
> +				opts->mmap_pages = KiB(256) / page_size;
> +		}
> +	}
> +
> +	/* Validate auxtrace_mmap_pages */
> +	if (opts->auxtrace_mmap_pages) {
> +		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
> +		size_t min_sz;
> +
> +		if (opts->auxtrace_snapshot_mode)
> +			min_sz = KiB(4);
> +		else
> +			min_sz = KiB(8);
> +
> +		if (sz < min_sz || !is_power_of_2(sz)) {
> +			pr_err("Invalid mmap size for Intel Processor Trace: must be at least %zuKiB and a power of 2\n",
> +			       min_sz / 1024);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/*
> +	 * To obtain the auxtrace buffer file descriptor, the auxtrace event
> +	 * must come first.
> +	 */
> +	evlist__to_front(evlist, rvtrace_evsel);
> +
> +	/*
> +	 * get the CPU on the sample - need it to associate trace ID in the
> +	 * AUX_OUTPUT_HW_ID event, and the AUX event for per-cpu mmaps.
> +	 */
> +	evsel__set_sample_bit(rvtrace_evsel, CPU);
> +
> +	/* Add dummy event to keep tracking */
> +	err = parse_event(evlist, "dummy:u");
> +	if (err)
> +		return err;
> +
> +	tracking_evsel = evlist__last(evlist);
> +	evlist__set_tracking_event(evlist, tracking_evsel);
> +
> +	tracking_evsel->core.attr.freq = 0;
> +	tracking_evsel->core.attr.sample_period = 1;
> +
> +	/* In per-cpu case, always need the time of mmap events etc */
> +	if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus))
> +		evsel__set_sample_bit(tracking_evsel, TIME);
> +
> +	return 0;
> +}
> +
> +static int rvtrace_snapshot_start(struct auxtrace_record *itr)
> +{
> +	struct rvtrace_recording *ptr =
> +			container_of(itr, struct rvtrace_recording, itr);
> +	struct evsel *evsel;
> +
> +	evlist__for_each_entry(ptr->evlist, evsel) {
> +		if (evsel->core.attr.type == ptr->rvtrace_pmu->type)
> +			return evsel__disable(evsel);
> +	}
> +	return -EINVAL;
> +}
> +
> +static int rvtrace_snapshot_finish(struct auxtrace_record *itr)
> +{
> +	struct rvtrace_recording *ptr =
> +			container_of(itr, struct rvtrace_recording, itr);
> +	struct evsel *evsel;
> +
> +	evlist__for_each_entry(ptr->evlist, evsel) {
> +		if (evsel->core.attr.type == ptr->rvtrace_pmu->type)
> +			return evsel__enable(evsel);
> +	}
> +	return -EINVAL;
> +}
> +
> +static u64 rvtrace_reference(struct auxtrace_record *itr __maybe_unused)
> +{
> +	return rdtsc();
> +}
> +
> +static void rvtrace_recording_free(struct auxtrace_record *itr)
> +{
> +	struct rvtrace_recording *ptr =
> +			container_of(itr, struct rvtrace_recording, itr);
> +
> +	free(ptr);
> +}
> +
> +static struct auxtrace_record *rvtrace_recording_init(int *err, struct perf_pmu *rvtrace_pmu)
> +{
> +	struct rvtrace_recording *ptr;
> +
> +	if (!rvtrace_pmu) {
> +		*err = -ENODEV;
> +		return NULL;
> +	}
> +
> +	ptr = zalloc(sizeof(*ptr));
> +	if (!ptr) {
> +		*err = -ENOMEM;
> +		return NULL;
> +	}
> +
> +	ptr->rvtrace_pmu = rvtrace_pmu;
> +	ptr->itr.parse_snapshot_options = rvtrace_parse_snapshot_options;
> +	ptr->itr.recording_options = rvtrace_recording_options;
> +	ptr->itr.info_priv_size = rvtrace_info_priv_size;
> +	ptr->itr.info_fill = rvtrace_info_fill;
> +	ptr->itr.snapshot_start = rvtrace_snapshot_start;
> +	ptr->itr.snapshot_finish = rvtrace_snapshot_finish;
> +	ptr->itr.free = rvtrace_recording_free;
> +	ptr->itr.reference = rvtrace_reference;
> +	ptr->itr.read_finish = auxtrace_record__read_finish;
> +	ptr->itr.alignment = 0;
> +
> +	*err = 0;
> +	return &ptr->itr;
> +}
> +
> +static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus,
> +					   int pmu_nr, struct evsel *evsel)
> +{
> +	int i;
> +
> +	if (!pmus)
> +		return NULL;
> +
> +	for (i = 0; i < pmu_nr; i++) {
> +		if (evsel->core.attr.type == pmus[i]->type)
> +			return pmus[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, int *err)
> +{
> +	struct perf_pmu	*rvtrace_pmu = NULL;
> +	struct perf_pmu *found_rvtrace = NULL;
> +	struct evsel *evsel;
> +
> +	if (!evlist)
> +		return NULL;
> +
> +	rvtrace_pmu = perf_pmus__find(RVTRACE_PMU_NAME);
> +	evlist__for_each_entry(evlist, evsel) {
> +		if (rvtrace_pmu && !found_rvtrace)
> +			found_rvtrace = find_pmu_for_event(&rvtrace_pmu, 1, evsel);
> +	}
> +
> +	if (found_rvtrace)
> +		return rvtrace_recording_init(err, rvtrace_pmu);
> +
> +	*err = 0;
> +	return NULL;
> +}
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index a224687ffbc1..944a43d48739 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -1411,6 +1411,7 @@ int perf_event__process_auxtrace_info(const struct perf_tool *tool __maybe_unuse
>   	case PERF_AUXTRACE_VPA_DTL:
>   		err = powerpc_vpadtl_process_auxtrace_info(event, session);
>   		break;
> +	case PERF_AUXTRACE_RISCV_TRACE:
>   	case PERF_AUXTRACE_UNKNOWN:
>   	default:
>   		return -EINVAL;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index 6947f3f284c0..4f4714c1b53f 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -46,6 +46,7 @@ enum auxtrace_type {
>   	PERF_AUXTRACE_S390_CPUMSF,
>   	PERF_AUXTRACE_HISI_PTT,
>   	PERF_AUXTRACE_VPA_DTL,
> +	PERF_AUXTRACE_RISCV_TRACE,
>   };
>   
>   enum itrace_period_type {
> diff --git a/tools/perf/util/rvtrace.h b/tools/perf/util/rvtrace.h
> new file mode 100644
> index 000000000000..1e48ed989dd7
> --- /dev/null
> +++ b/tools/perf/util/rvtrace.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright(C) 2026 Spacemit Limited. All rights reserved.
> + */
> +
> +#ifndef INCLUDE__UTIL_PERF_RVTRACE_H__
> +#define INCLUDE__UTIL_PERF_RVTRACE_H__
> +
> +#include "debug.h"
> +#include "auxtrace.h"
> +#include "util/event.h"
> +#include "util/session.h"
> +#include <linux/bits.h>
> +
> +enum {
> +	/* PMU->type (32 bit), total # of CPUs (32 bit) */
> +	RVTRACE_PMU_TYPE_CPUS,
> +	RVTRACE_HEADER_MAX,
> +};
> +
> +/* Trace Encoder metadata */
> +enum {
> +	RVTRACE_ENCODER_CPU,
> +	RVTRACE_ENCODER_NR_TRC_PARAMS,
> +	RVTRACE_ENCODER_FORMAT,
> +	RVTRACE_ENCODER_CONTEXT,
> +	RVTRACE_ENCODER_INHB_SRC,
> +	RVTRACE_ENCODER_SRCBITS,
> +	RVTRACE_ENCODER_SRCID,
> +	RVTRACE_ENCODER_PRIV_MAX,
> +};
> +
> +#define RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH (RVTRACE_ENCODER_PRIV_MAX - RVTRACE_ENCODER_FORMAT)
> +
> +#define RVTRACE_HEADER_SIZE		(RVTRACE_HEADER_MAX * sizeof(u64))
> +#define RVTRACE_ENCODER_PRIV_SIZE	(RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
> +
> +#endif


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-14  9:04   ` Zane Leung
@ 2026-04-15  0:10     ` Bo Gan
  2026-04-15  1:23       ` Zane Leung
  0 siblings, 1 reply; 20+ messages in thread
From: Bo Gan @ 2026-04-15  0:10 UTC (permalink / raw)
  To: Zane Leung, Anup Patel
  Cc: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers, coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, mchitale, atish.patra, andrew.jones, sunilvl,
	linux-riscv, linux-kernel, anup.patel, mayuresh.chitale,
	zhuangqiubin, suzuki.poulose, mike.leach, james.clark,
	linux-arm-kernel

Hi Zane, Anup,

On 4/14/26 02:04, Zane Leung wrote:
> 
> On 4/14/2026 3:23 PM, Anup Patel wrote:
>> On Tue, Apr 14, 2026 at 9:12 AM Zane Leung <liangzhen@linux.spacemit.com> wrote:
>>> From: liangzhen <zhen.liang@spacemit.com>
>>>
>>> This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
>>> trace drivers within the CoreSight framework and integrating them with perf tools.
>>> The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
>>> components, which can be directly leveraged through the existing CoreSight infrastructure.
>>>
>>> Linux RISC-V trace support form Anup Patel:
>>> (https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
>>> which currently lacks ATB component support and guidance on reusing CoreSight components.
>> What stops you from adding RISC-V trace funnel and ATB bridge drivers
>> on top of this series ?
>>
> Firstly, my works started much earlier than this series. Secondly, it is difficult to add the coresight funnel and coresight tmc components to this series. Based on the coresight framework, I can directly reuse them.
> 
>>
>>> The series includes:
>>> - RISC-V trace driver implementation within the CoreSight framework
>> The RISC-V trace very different from the CoreSight framework in many ways:
>> 1) Types of components supported
>> 2) Trace packet formats
>> 3) The way MMIO based components are discoverd
>> 4) ... and more ...
> 1) I believe that RISC-V tracing is coresight-alike, where have encoders/funnel/sink/bridge that are described through DT and controlled by MMIO.
> 2) I think the difference in package format is nothing more than the coresight frame generated by the ATB component to distinguish the trace source. After removing it, it becomes riscv trace data.
> 3) The current CoreSight framework code does not introduce this mechanism, it is described through DT.
>>> - RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
>>> - RISC-V trace PMU record capabilities and parsing events in perf.
>>> - RISC-V Nexus Trace decoder for perf tools
>>>
>>> Any comments or suggestions are welcome.
>>>
>>> Verification on K3 SoC:
>>> To verify this patch series on K3 hardware, the following device tree are required:
>>> 1. RISC-V Trace Encoder node (8)
>>> 2. RISC-V ATB Bridge node (8)
>>> 3. RISC-V Trace Funnel node (2)
>>> 3. CoreSight Funnel configuration for RISC-V (1)
>>> 4. CoreSight TMC configuration for trace buffer (1)
>>>
>>> /{
>>>          dummy_clk: apb-pclk {
>>>                  compatible = "fixed-clock";
>>>                  #clock-cells = <0x0>;
>>>                  clock-output-names = "clk14mhz";
>>>                  clock-frequency = <14000000>;
>>>          };
>>>
>>>
>>>          soc: soc {
>>>                  #address-cells = <2>;
>>>                  #size-cells = <2>;
>>>
>>>                  encoder0: encoder@d9002000 {
>>>                          compatible = "riscv,trace-encoder";
>>>                          reg = <0x0 0xd9002000 0x0 0x1000>;
>>>                          cpus = <&cpu_0>;
>>>                          out-ports {
>>>                                  port {
>>>                                          cluster0_encoder0_out_port: endpoint {
>>>                                                  remote-endpoint = <&cluster0_bridge0_in_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>>                  bridge0: bridge@d9003000 {
>>>                          compatible = "riscv,trace-atbbridge";
>>>                          reg = <0x0 0xd9003000 0x0 0x1000>;
>>>                          cpus = <&cpu_0>;
>>>                          out-ports {
>>>                                  port {
>>>                                          cluster0_bridge0_out_port: endpoint {
>>>                                                  remote-endpoint = <&cluster0_funnel_in_port0>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                          in-ports {
>>>                                  port {
>>>                                          cluster0_bridge0_in_port: endpoint {
>>>                                                  remote-endpoint = <&cluster0_encoder0_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>> ...
>>>
>>>                  cluster0_funnel: funnel@d9000000 {
>>>                          compatible = "riscv,trace-funnel";
>>>                          reg = <0x0 0xd9000000 0x0 0x1000>;
>>>                          cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
>>>                          riscv,timestamp-present;
>>>                          out-ports {
>>>                                  port {
>>>                                          cluster0_funnel_out_port: endpoint {
>>>                                                  remote-endpoint = <&main_funnel_in_port0>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                          in-ports {
>>>                                  #address-cells = <1>;
>>>                                  #size-cells = <0>;
>>>
>>>                                  port@0 {
>>>                                          reg = <0>;
>>>                                          cluster0_funnel_in_port0: endpoint {
>>>                                                  remote-endpoint = <&cluster0_bridge0_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@1 {
>>>                                          reg = <1>;
>>>                                          cluster0_funnel_in_port1: endpoint {
>>>                                                  remote-endpoint = <&cluster0_bridge1_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@2 {
>>>                                          reg = <2>;
>>>                                          cluster0_funnel_in_port2: endpoint {
>>>                                                  remote-endpoint = <&cluster0_bridge2_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@3 {
>>>                                          reg = <3>;
>>>                                          cluster0_funnel_in_port3: endpoint {
>>>                                                  remote-endpoint = <&cluster0_bridge3_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>>                  cluster1_funnel: funnel@d9010000 {
>>>                          compatible = "riscv,trace-funnel";
>>>                          reg = <0x0 0xd9010000 0x0 0x1000>;
>>>                          cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
>>>                          riscv,timestamp-present;
>>>                          out-ports {
>>>                                  port {
>>>                                          cluster1_funnel_out_port: endpoint {
>>>                                                  remote-endpoint = <&main_funnel_in_port1>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                          in-ports {
>>>                                  #address-cells = <1>;
>>>                                  #size-cells = <0>;
>>>
>>>                                  port@0 {
>>>                                          reg = <0>;
>>>                                          cluster1_funnel_in_port0: endpoint {
>>>                                                  remote-endpoint = <&cluster1_bridge0_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@1 {
>>>                                          reg = <1>;
>>>                                          cluster1_funnel_in_port1: endpoint {
>>>                                                  remote-endpoint = <&cluster1_bridge1_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@2 {
>>>                                          reg = <2>;
>>>                                          cluster1_funnel_in_port2: endpoint {
>>>                                                  remote-endpoint = <&cluster1_bridge2_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@3 {
>>>                                          reg = <3>;
>>>                                          cluster1_funnel_in_port3: endpoint {
>>>                                                  remote-endpoint = <&cluster1_bridge3_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>>                  main_funnel: funnel@d9042000 {
>>>                          compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
>> Is it legally allowed to mix and match ARM coresight IPs with
>> RISC-V trace components at hardware level ?
> The ATB Bridge allows sending RISC-V trace to Arm CoreSight infrastructure (instead of RISC-V compliant sink defined in this document) as an ATB initiator.  see:
> https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L184
> For ATB Bridge, read trace using Coresight components (ETB/TMC/TPIU). see:
> https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L1684
>>
>>>                          reg = <0x0 0xd9042000 0x0 0x1000>;
>>>                          clocks = <&dummy_clk>;
>>>                          clock-names = "apb_pclk";
>>>                          out-ports {
>>>                                  port {
>>>                                          main_funnel_out_port: endpoint {
>>>                                                  remote-endpoint = <&etf_in_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                          in-ports {
>>>                                  #address-cells = <1>;
>>>                                  #size-cells = <0>;
>>>
>>>                                  port@0 {
>>>                                          reg = <0>;
>>>                                          main_funnel_in_port0: endpoint {
>>>                                                  remote-endpoint = <&cluster0_funnel_out_port>;
>>>                                          };
>>>                                  };
>>>
>>>                                  port@1 {
>>>                                          reg = <1>;
>>>                                          main_funnel_in_port1: endpoint {
>>>                                                  remote-endpoint = <&cluster1_funnel_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>>                  etf: etf@d9043000 {
>>>                          compatible = "arm,coresight-tmc", "arm,primecell";
>>>                          reg = <0x0 0xd9043000 0x0 0x1000>;
>>>                          clocks = <&dummy_clk>;
>>>                          clock-names = "apb_pclk";
>>>                          out-ports {
>>>                                  port {
>>>                                          etf_out_port: endpoint {
>>>                                                  remote-endpoint = <&etr_in_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                          in-ports {
>>>                                  port {
>>>                                          etf_in_port: endpoint {
>>>                                                  remote-endpoint = <&main_funnel_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>
>>>                  etr: etr@d9044000 {
>>>                          compatible = "arm,coresight-tmc", "arm,primecell";
>>>                          reg = <0x0 0xd9044000 0x0 0x1000>;
>>>                          clocks = <&dummy_clk>;
>>>                          clock-names = "apb_pclk";
>>>                          arm,scatter-gather;
>>>                          in-ports {
>>>                                  port {
>>>                                          etr_in_port: endpoint {
>>>                                                  remote-endpoint = <&etf_out_port>;
>>>                                          };
>>>                                  };
>>>                          };
>>>                  };
>>>          };
>>> };
>>>
>>> Verification case:
>>>
>>> ~ # perf list pmu
>>>    rvtrace//                                          [Kernel PMU event]
>>>
>>> ~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
>>> Linux
>>> [ perf record: Woken up 1 times to write data ]
>>> [ perf record: Captured and wrote 0.191 MB perf.data ]
>>> ~ # perf script
>>>             uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
>>>             uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
>>> ...
>>>
>>> liangzhen (12):
>>>    coresight: Add RISC-V support to CoreSight tracing
>>>    coresight: Initial implementation of RISC-V trace driver
>>>    coresight: Add RISC-V Trace Encoder driver
>>>    coresight: Add RISC-V Trace Funnel driver
>>>    coresight: Add RISC-V Trace ATB Bridge driver
>>>    coresight rvtrace: Add timestamp component support for encoder and
>>>      funnel
>>>    coresight: Add RISC-V PMU name support
>>>    perf tools: riscv: making rvtrace PMU listable
>>>    perf tools: Add RISC-V trace PMU record capabilities
>>>    perf tools: Add Nexus RISC-V Trace decoder
>>>    perf symbols: Add RISC-V PLT entry sizes
>>>    perf tools: Integrate RISC-V trace decoder into auxtrace
>>>
>>>   drivers/hwtracing/Kconfig                     |    2 +
>>>   drivers/hwtracing/coresight/Kconfig           |   46 +-
>>>   drivers/hwtracing/coresight/Makefile          |    6 +
>>>   drivers/hwtracing/coresight/coresight-core.c  |    8 +
>>>   .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
>>>   .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
>>>   .../hwtracing/coresight/coresight-platform.c  |    1 -
>>>   .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
>>>   .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
>>>   .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
>>>   drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
>>>   .../coresight/rvtrace-encoder-core.c          |  562 +++++++
>>>   .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
>>>   drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
>>>   drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
>>>   drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
>>>   .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
>>>   .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
>>>   include/linux/coresight-pmu.h                 |    4 +
>>>   include/linux/rvtrace.h                       |  116 ++
>>>   tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
>>>   tools/perf/arch/riscv/util/Build              |    2 +
>>>   tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
>>>   tools/perf/arch/riscv/util/pmu.c              |   20 +
>>>   tools/perf/util/Build                         |    3 +
>>>   tools/perf/util/auxtrace.c                    |    4 +
>>>   tools/perf/util/auxtrace.h                    |    1 +
>>>   tools/perf/util/nexus-rv-decoder/Build        |    1 +
>>>   .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
>>>   .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
>>>   .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
>>>   tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
>>>   tools/perf/util/rvtrace.h                     |   40 +
>>>   tools/perf/util/symbol-elf.c                  |    4 +
>>>   34 files changed, 6320 insertions(+), 3 deletions(-)
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
>>>   create mode 100644 include/linux/rvtrace.h
>>>   create mode 100644 tools/arch/riscv/include/asm/insn.h
>>>   create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>>>   create mode 100644 tools/perf/arch/riscv/util/pmu.c
>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/Build
>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
>>>   create mode 100644 tools/perf/util/rvtrace-decoder.c
>>>   create mode 100644 tools/perf/util/rvtrace.h
>>>
>>> --
>>> 2.34.1
>>>
>> NACK to this approach of retrofitting RISC-V trace into ARM coresight.
> I agree that integrating RISC-V trace directly into CoreSight is not a good approach, so I think we should abstract some of the logic of coresight and reuse it in RISC-V Trace.

I think we should indeed abstract out some coresight logic so it can be
reused by both coresight/rvtrace, especially the way of handling trace
component topology. The generic logic of building a path from source ->
sink should be common among coresight/rvtrace and for future architecture.
To support ATB use case like the K3, I think the best way to approach this
is to model the atb driver as a separate rvtrace-atb drive and let it
drive the ARM coresight components. Don't treat risc-v trace as another
type of coresight.

@Zane, can you also clarify that only the 2 X100 clusters have trace
encoders, and the A100 clusters do not? I don't see the 2 A100 clusters
listed in your sample devicetree. If that's the case, then we'll be faced
with heterogeneous trace setup, making it even more complicated.

>>
>> Regards,
>> Anup
> 
> Thanks,
> Zane

Bo

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight
  2026-04-15  0:10     ` Bo Gan
@ 2026-04-15  1:23       ` Zane Leung
  0 siblings, 0 replies; 20+ messages in thread
From: Zane Leung @ 2026-04-15  1:23 UTC (permalink / raw)
  To: Bo Gan, Anup Patel
  Cc: robh, krzk+dt, conor+dt, palmer, pjw, gregkh, alexander.shishkin,
	irogers, coresight, peterz, mingo, namhyung, mark.rutland, jolsa,
	adrian.hunter, mchitale, atish.patra, andrew.jones, sunilvl,
	linux-riscv, linux-kernel, anup.patel, mayuresh.chitale,
	zhuangqiubin, suzuki.poulose, mike.leach, james.clark,
	linux-arm-kernel

Hi Bo,

On 4/15/2026 8:10 AM, Bo Gan wrote:
> Hi Zane, Anup,
>
> On 4/14/26 02:04, Zane Leung wrote:
>>
>> On 4/14/2026 3:23 PM, Anup Patel wrote:
>>> On Tue, Apr 14, 2026 at 9:12 AM Zane Leung <liangzhen@linux.spacemit.com> wrote:
>>>> From: liangzhen <zhen.liang@spacemit.com>
>>>>
>>>> This series adds Linux RISC-V trace support via CoreSight, implementing RISC-V
>>>> trace drivers within the CoreSight framework and integrating them with perf tools.
>>>> The K3 SoC contains RISC-V Encoder, Funnel, ATB, CoreSight Funnel, and CoreSight TMC
>>>> components, which can be directly leveraged through the existing CoreSight infrastructure.
>>>>
>>>> Linux RISC-V trace support form Anup Patel:
>>>> (https://patchwork.kernel.org/project/linux-riscv/cover/20260225062448.4027948-1-anup.patel@oss.qualcomm.com/)
>>>> which currently lacks ATB component support and guidance on reusing CoreSight components.
>>> What stops you from adding RISC-V trace funnel and ATB bridge drivers
>>> on top of this series ?
>>>
>> Firstly, my works started much earlier than this series. Secondly, it is difficult to add the coresight funnel and coresight tmc components to this series. Based on the coresight framework, I can directly reuse them.
>>
>>>
>>>> The series includes:
>>>> - RISC-V trace driver implementation within the CoreSight framework
>>> The RISC-V trace very different from the CoreSight framework in many ways:
>>> 1) Types of components supported
>>> 2) Trace packet formats
>>> 3) The way MMIO based components are discoverd
>>> 4) ... and more ...
>> 1) I believe that RISC-V tracing is coresight-alike, where have encoders/funnel/sink/bridge that are described through DT and controlled by MMIO.
>> 2) I think the difference in package format is nothing more than the coresight frame generated by the ATB component to distinguish the trace source. After removing it, it becomes riscv trace data.
>> 3) The current CoreSight framework code does not introduce this mechanism, it is described through DT.
>>>> - RISC-V Trace Encoder, Funnel, and ATB Bridge drivers as CoreSight devices
>>>> - RISC-V trace PMU record capabilities and parsing events in perf.
>>>> - RISC-V Nexus Trace decoder for perf tools
>>>>
>>>> Any comments or suggestions are welcome.
>>>>
>>>> Verification on K3 SoC:
>>>> To verify this patch series on K3 hardware, the following device tree are required:
>>>> 1. RISC-V Trace Encoder node (8)
>>>> 2. RISC-V ATB Bridge node (8)
>>>> 3. RISC-V Trace Funnel node (2)
>>>> 3. CoreSight Funnel configuration for RISC-V (1)
>>>> 4. CoreSight TMC configuration for trace buffer (1)
>>>>
>>>> /{
>>>>          dummy_clk: apb-pclk {
>>>>                  compatible = "fixed-clock";
>>>>                  #clock-cells = <0x0>;
>>>>                  clock-output-names = "clk14mhz";
>>>>                  clock-frequency = <14000000>;
>>>>          };
>>>>
>>>>
>>>>          soc: soc {
>>>>                  #address-cells = <2>;
>>>>                  #size-cells = <2>;
>>>>
>>>>                  encoder0: encoder@d9002000 {
>>>>                          compatible = "riscv,trace-encoder";
>>>>                          reg = <0x0 0xd9002000 0x0 0x1000>;
>>>>                          cpus = <&cpu_0>;
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          cluster0_encoder0_out_port: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_bridge0_in_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>>                  bridge0: bridge@d9003000 {
>>>>                          compatible = "riscv,trace-atbbridge";
>>>>                          reg = <0x0 0xd9003000 0x0 0x1000>;
>>>>                          cpus = <&cpu_0>;
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          cluster0_bridge0_out_port: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_funnel_in_port0>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                          in-ports {
>>>>                                  port {
>>>>                                          cluster0_bridge0_in_port: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_encoder0_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>> ...
>>>>
>>>>                  cluster0_funnel: funnel@d9000000 {
>>>>                          compatible = "riscv,trace-funnel";
>>>>                          reg = <0x0 0xd9000000 0x0 0x1000>;
>>>>                          cpus = <&cpu_0>, <&cpu_1>, <&cpu_2>, <&cpu_3>;
>>>>                          riscv,timestamp-present;
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          cluster0_funnel_out_port: endpoint {
>>>>                                                  remote-endpoint = <&main_funnel_in_port0>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                          in-ports {
>>>>                                  #address-cells = <1>;
>>>>                                  #size-cells = <0>;
>>>>
>>>>                                  port@0 {
>>>>                                          reg = <0>;
>>>>                                          cluster0_funnel_in_port0: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_bridge0_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@1 {
>>>>                                          reg = <1>;
>>>>                                          cluster0_funnel_in_port1: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_bridge1_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@2 {
>>>>                                          reg = <2>;
>>>>                                          cluster0_funnel_in_port2: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_bridge2_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@3 {
>>>>                                          reg = <3>;
>>>>                                          cluster0_funnel_in_port3: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_bridge3_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>>                  cluster1_funnel: funnel@d9010000 {
>>>>                          compatible = "riscv,trace-funnel";
>>>>                          reg = <0x0 0xd9010000 0x0 0x1000>;
>>>>                          cpus = <&cpu_4>, <&cpu_5>, <&cpu_6>, <&cpu_7>;
>>>>                          riscv,timestamp-present;
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          cluster1_funnel_out_port: endpoint {
>>>>                                                  remote-endpoint = <&main_funnel_in_port1>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                          in-ports {
>>>>                                  #address-cells = <1>;
>>>>                                  #size-cells = <0>;
>>>>
>>>>                                  port@0 {
>>>>                                          reg = <0>;
>>>>                                          cluster1_funnel_in_port0: endpoint {
>>>>                                                  remote-endpoint = <&cluster1_bridge0_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@1 {
>>>>                                          reg = <1>;
>>>>                                          cluster1_funnel_in_port1: endpoint {
>>>>                                                  remote-endpoint = <&cluster1_bridge1_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@2 {
>>>>                                          reg = <2>;
>>>>                                          cluster1_funnel_in_port2: endpoint {
>>>>                                                  remote-endpoint = <&cluster1_bridge2_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@3 {
>>>>                                          reg = <3>;
>>>>                                          cluster1_funnel_in_port3: endpoint {
>>>>                                                  remote-endpoint = <&cluster1_bridge3_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>>                  main_funnel: funnel@d9042000 {
>>>>                          compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
>>> Is it legally allowed to mix and match ARM coresight IPs with
>>> RISC-V trace components at hardware level ?
>> The ATB Bridge allows sending RISC-V trace to Arm CoreSight infrastructure (instead of RISC-V compliant sink defined in this document) as an ATB initiator.  see:
>> https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L184
>> For ATB Bridge, read trace using Coresight components (ETB/TMC/TPIU). see:
>> https://github.com/riscv-non-isa/riscv-nexus-trace/blob/105dc1c349556622e4d202d22b584a887ded462f/docs/RISC-V-Trace-Control-Interface.adoc#L1684
>>>
>>>>                          reg = <0x0 0xd9042000 0x0 0x1000>;
>>>>                          clocks = <&dummy_clk>;
>>>>                          clock-names = "apb_pclk";
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          main_funnel_out_port: endpoint {
>>>>                                                  remote-endpoint = <&etf_in_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                          in-ports {
>>>>                                  #address-cells = <1>;
>>>>                                  #size-cells = <0>;
>>>>
>>>>                                  port@0 {
>>>>                                          reg = <0>;
>>>>                                          main_funnel_in_port0: endpoint {
>>>>                                                  remote-endpoint = <&cluster0_funnel_out_port>;
>>>>                                          };
>>>>                                  };
>>>>
>>>>                                  port@1 {
>>>>                                          reg = <1>;
>>>>                                          main_funnel_in_port1: endpoint {
>>>>                                                  remote-endpoint = <&cluster1_funnel_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>>                  etf: etf@d9043000 {
>>>>                          compatible = "arm,coresight-tmc", "arm,primecell";
>>>>                          reg = <0x0 0xd9043000 0x0 0x1000>;
>>>>                          clocks = <&dummy_clk>;
>>>>                          clock-names = "apb_pclk";
>>>>                          out-ports {
>>>>                                  port {
>>>>                                          etf_out_port: endpoint {
>>>>                                                  remote-endpoint = <&etr_in_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                          in-ports {
>>>>                                  port {
>>>>                                          etf_in_port: endpoint {
>>>>                                                  remote-endpoint = <&main_funnel_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>
>>>>                  etr: etr@d9044000 {
>>>>                          compatible = "arm,coresight-tmc", "arm,primecell";
>>>>                          reg = <0x0 0xd9044000 0x0 0x1000>;
>>>>                          clocks = <&dummy_clk>;
>>>>                          clock-names = "apb_pclk";
>>>>                          arm,scatter-gather;
>>>>                          in-ports {
>>>>                                  port {
>>>>                                          etr_in_port: endpoint {
>>>>                                                  remote-endpoint = <&etf_out_port>;
>>>>                                          };
>>>>                                  };
>>>>                          };
>>>>                  };
>>>>          };
>>>> };
>>>>
>>>> Verification case:
>>>>
>>>> ~ # perf list pmu
>>>>    rvtrace//                                          [Kernel PMU event]
>>>>
>>>> ~ # perf record -e rvtrace/@tmc_etr0/ --per-thread uname
>>>> Linux
>>>> [ perf record: Woken up 1 times to write data ]
>>>> [ perf record: Captured and wrote 0.191 MB perf.data ]
>>>> ~ # perf script
>>>>             uname     137 [003]          1           branches:  ffffffff80931470 rvtrace_poll_bit+0x38 ([kernel.kallsyms]) => ffffffff80931492 rvtrace_poll_bit+0x5a ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff809328a6 encoder_enable_hw+0x252 ([kernel.kallsyms]) => ffffffff809328ba encoder_enable_hw+0x266 ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff80932c4a encoder_enable+0x82 ([kernel.kallsyms]) => ffffffff80932c36 encoder_enable+0x6e ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff80928198 etm_event_start+0xf0 ([kernel.kallsyms]) => ffffffff809281aa etm_event_start+0x102 ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff809281e6 etm_event_start+0x13e ([kernel.kallsyms]) => ffffffff8092755e coresight_get_sink_id+0x16 ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff8092820e etm_event_start+0x166 ([kernel.kallsyms]) => ffffffff80928226 etm_event_start+0x17e ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff801c3bb4 perf_report_aux_output_id+0x0 ([kernel.kallsyms]) => ffffffff801c3bd6 perf_report_aux_output_id+0x22 ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff801c3c5a perf_report_aux_output_id+0xa6 ([kernel.kallsyms]) => ffffffff801c3bf0 perf_report_aux_output_id+0x3c ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff801c3c40 perf_report_aux_output_id+0x8c ([kernel.kallsyms]) => ffffffff801c3aea __perf_event_header__init_id+0x2a ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff801c3b42 __perf_event_header__init_id+0x82 ([kernel.kallsyms]) => ffffffff801c3b4a __perf_event_header__init_id+0x8a ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff801c3bb0 __perf_event_header__init_id+0xf0 ([kernel.kallsyms]) => ffffffff801c3b58 __perf_event_header__init_id+0x98 ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff8004c658 __task_pid_nr_ns+0x0 ([kernel.kallsyms]) => ffffffff8004c696 __task_pid_nr_ns+0x3e ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff8004c71e __task_pid_nr_ns+0xc6 ([kernel.kallsyms]) => ffffffff8004c6a4 __task_pid_nr_ns+0x4c ([kernel.kallsyms])
>>>>             uname     137 [003]          1           branches:  ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms]) => ffffffff8004c6e4 __task_pid_nr_ns+0x8c ([kernel.kallsyms])
>>>> ...
>>>>
>>>> liangzhen (12):
>>>>    coresight: Add RISC-V support to CoreSight tracing
>>>>    coresight: Initial implementation of RISC-V trace driver
>>>>    coresight: Add RISC-V Trace Encoder driver
>>>>    coresight: Add RISC-V Trace Funnel driver
>>>>    coresight: Add RISC-V Trace ATB Bridge driver
>>>>    coresight rvtrace: Add timestamp component support for encoder and
>>>>      funnel
>>>>    coresight: Add RISC-V PMU name support
>>>>    perf tools: riscv: making rvtrace PMU listable
>>>>    perf tools: Add RISC-V trace PMU record capabilities
>>>>    perf tools: Add Nexus RISC-V Trace decoder
>>>>    perf symbols: Add RISC-V PLT entry sizes
>>>>    perf tools: Integrate RISC-V trace decoder into auxtrace
>>>>
>>>>   drivers/hwtracing/Kconfig                     |    2 +
>>>>   drivers/hwtracing/coresight/Kconfig           |   46 +-
>>>>   drivers/hwtracing/coresight/Makefile          |    6 +
>>>>   drivers/hwtracing/coresight/coresight-core.c  |    8 +
>>>>   .../hwtracing/coresight/coresight-etm-perf.c  |    1 -
>>>>   .../hwtracing/coresight/coresight-etm-perf.h  |   21 +
>>>>   .../hwtracing/coresight/coresight-platform.c  |    1 -
>>>>   .../hwtracing/coresight/coresight-tmc-etf.c   |    4 +
>>>>   .../hwtracing/coresight/coresight-tmc-etr.c   |    4 +
>>>>   .../hwtracing/coresight/rvtrace-atbbridge.c   |  239 +++
>>>>   drivers/hwtracing/coresight/rvtrace-core.c    |  135 ++
>>>>   .../coresight/rvtrace-encoder-core.c          |  562 +++++++
>>>>   .../coresight/rvtrace-encoder-sysfs.c         |  363 +++++
>>>>   drivers/hwtracing/coresight/rvtrace-encoder.h |  151 ++
>>>>   drivers/hwtracing/coresight/rvtrace-funnel.c  |  337 ++++
>>>>   drivers/hwtracing/coresight/rvtrace-funnel.h  |   39 +
>>>>   .../hwtracing/coresight/rvtrace-timestamp.c   |  278 ++++
>>>>   .../hwtracing/coresight/rvtrace-timestamp.h   |   64 +
>>>>   include/linux/coresight-pmu.h                 |    4 +
>>>>   include/linux/rvtrace.h                       |  116 ++
>>>>   tools/arch/riscv/include/asm/insn.h           |  645 ++++++++
>>>>   tools/perf/arch/riscv/util/Build              |    2 +
>>>>   tools/perf/arch/riscv/util/auxtrace.c         |  490 ++++++
>>>>   tools/perf/arch/riscv/util/pmu.c              |   20 +
>>>>   tools/perf/util/Build                         |    3 +
>>>>   tools/perf/util/auxtrace.c                    |    4 +
>>>>   tools/perf/util/auxtrace.h                    |    1 +
>>>>   tools/perf/util/nexus-rv-decoder/Build        |    1 +
>>>>   .../util/nexus-rv-decoder/nexus-rv-decoder.c  | 1364 +++++++++++++++++
>>>>   .../util/nexus-rv-decoder/nexus-rv-decoder.h  |  139 ++
>>>>   .../perf/util/nexus-rv-decoder/nexus-rv-msg.h |  190 +++
>>>>   tools/perf/util/rvtrace-decoder.c             | 1039 +++++++++++++
>>>>   tools/perf/util/rvtrace.h                     |   40 +
>>>>   tools/perf/util/symbol-elf.c                  |    4 +
>>>>   34 files changed, 6320 insertions(+), 3 deletions(-)
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-atbbridge.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-core.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-core.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder-sysfs.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-encoder.h
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-funnel.h
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.c
>>>>   create mode 100644 drivers/hwtracing/coresight/rvtrace-timestamp.h
>>>>   create mode 100644 include/linux/rvtrace.h
>>>>   create mode 100644 tools/arch/riscv/include/asm/insn.h
>>>>   create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
>>>>   create mode 100644 tools/perf/arch/riscv/util/pmu.c
>>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/Build
>>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
>>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
>>>>   create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
>>>>   create mode 100644 tools/perf/util/rvtrace-decoder.c
>>>>   create mode 100644 tools/perf/util/rvtrace.h
>>>>
>>>> -- 
>>>> 2.34.1
>>>>
>>> NACK to this approach of retrofitting RISC-V trace into ARM coresight.
>> I agree that integrating RISC-V trace directly into CoreSight is not a good approach, so I think we should abstract some of the logic of coresight and reuse it in RISC-V Trace.
>
> I think we should indeed abstract out some coresight logic so it can be
> reused by both coresight/rvtrace, especially the way of handling trace
> component topology. The generic logic of building a path from source ->
> sink should be common among coresight/rvtrace and for future architecture.
> To support ATB use case like the K3, I think the best way to approach this
> is to model the atb driver as a separate rvtrace-atb drive and let it
> drive the ARM coresight components. Don't treat risc-v trace as another
> type of coresight.
>
> @Zane, can you also clarify that only the 2 X100 clusters have trace
> encoders, and the A100 clusters do not? I don't see the 2 A100 clusters
> listed in your sample devicetree. If that's the case, then we'll be faced
> with heterogeneous trace setup, making it even more complicated.
>
The A100 clusters also have trace encoders, just like the X100 clusters. The reason I didn't include the two A100 clusters in the sample devicetree is to maintain consistency with the upstream k3.dtsi.


>>>
>>> Regards,
>>> Anup
>>
>> Thanks,
>> Zane
>
> Bo
>

Thanks,

Zane

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-04-15  1:24 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-14  3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
2026-04-14  3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
2026-04-14  3:41 ` [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Zane Leung
2026-04-14  3:41 ` [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Zane Leung
2026-04-14  3:41 ` [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Zane Leung
2026-04-14  3:41 ` [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Zane Leung
2026-04-14  3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
2026-04-14 23:31   ` Bo Gan
2026-04-14  3:41 ` [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
2026-04-14  3:41 ` [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Zane Leung
2026-04-14  3:41 ` [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace Zane Leung
2026-04-14  4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
2026-04-14  8:08   ` Zane Leung
2026-04-14  7:23 ` Anup Patel
2026-04-14  9:04   ` Zane Leung
2026-04-15  0:10     ` Bo Gan
2026-04-15  1:23       ` Zane Leung

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox