Skip to contentSkip to conversation

Linux RISC-V trace framework and drivers

PATCH Superseded Anup Patel 7 participants 36 messages Oct 2, 2025 – Mar 13 2 messages not in the archive

Subsystems (4)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Anup Patel

author
#0 lore raw

cover Linux RISC-V trace framework and drivers

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

In ther future, there will be subsequent series adding:
1) Sysfs support
2) ACPI support
3) More trace drivers (such as funnel, ATB, etc)
4) Support for upcoming self-hosted trace specification
5) ... and more ...

These patches can also be found in the riscv_trace_support_v1 branch at:
https://github.com/avpatel/linux.git

To test the patches, we need QEMU virt machine with RISC-V trace support which can be found in rv-etrace branch at:
https://gitlab.com/danielhb/qemu.git

To capture rvtrace data using perf on QEMU virt machine do the following:
1) Launch QEMU virt machine

   $ qemu-system-riscv64 -nographic -M virt -smp 2 -bios fw_dynamic.bin \
     -kernel Image -append "root=/dev/vda rw console=ttyS0 earlycon=sbi" \
     -drive file=/path/to/rootfs.img,id=disk1,if=none,format=raw \
     -device virtio-blk-device,drive=disk1
2) Run perf record to capture rvtrace data
   $ perf record --all-cpus -e rvtrace/event=0x1/ <command>
3) The step2 would create a perf.data file which has the rvtrace data.
   Now run perf report -D and look for PERF_RECORD_AUXTRACE event
   section(s) which point(s) to the actual rvtrace data offset.

Anup Patel (5):
dt-bindings: Add RISC-V trace component bindings
rvtrace: Initial implementation of driver framework
rvtrace: Add functions to create/destroy a trace component path
rvtrace: Add function to copy into perf AUX buffer
MAINTAINERS: Add entry for RISC-V trace framework and drivers

Mayuresh Chitale (6):
rvtrace: Add functions to start/stop tracing on a component path
rvtrace: Add trace encoder driver
rvtrace: Add trace ramsink driver
rvtrace: Add perf driver for tracing using perf tool perf tools: Add RISC-V trace PMU record capabilities perf tools: Initial support for RISC-V trace decoder

 .../bindings/riscv/riscv,trace-component.yaml | 110 +++
 MAINTAINERS                                   |  11 +
 drivers/Makefile                              |   1 +
 drivers/hwtracing/Kconfig                     |   2 +
 drivers/hwtracing/rvtrace/Kconfig             |  32 +
 drivers/hwtracing/rvtrace/Makefile            |   6 +
 drivers/hwtracing/rvtrace/rvtrace-core.c      | 781 ++++++++++++++++++
 drivers/hwtracing/rvtrace/rvtrace-encoder.c   | 107 +++
 drivers/hwtracing/rvtrace/rvtrace-perf.c      | 343 ++++++++
 drivers/hwtracing/rvtrace/rvtrace-platform.c  | 174 ++++
 drivers/hwtracing/rvtrace/rvtrace-ramsink.c   | 198 +++++
 include/linux/rvtrace.h                       | 341 ++++++++
 tools/perf/arch/riscv/util/Build              |   1 +
 tools/perf/arch/riscv/util/auxtrace.c         | 218 +++++
 tools/perf/util/Build                         |   1 +
 tools/perf/util/auxtrace.c                    |   4 +
 tools/perf/util/auxtrace.h                    |   1 +
 tools/perf/util/rvtrace-decoder.c             |  91 ++
 tools/perf/util/rvtrace.h                     |  20 +
 19 files changed, 2442 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml
 create mode 100644 drivers/hwtracing/rvtrace/Kconfig
 create mode 100644 drivers/hwtracing/rvtrace/Makefile
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-encoder.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-perf.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c
 create mode 100644 include/linux/rvtrace.h
 create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
 create mode 100644 tools/perf/util/rvtrace-decoder.c
 create mode 100644 tools/perf/util/rvtrace.h
Signature · 1 line
2.43.0
author
#1 lore raw

PATCH 1/11 dt-bindings: Add RISC-V trace component bindings

Add device tree bindings for the memory mapped RISC-V trace components which support both the RISC-V efficient trace (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.

The RISC-V trace components are defined by the RISC-V trace control interface specification.

---
 .../bindings/riscv/riscv,trace-component.yaml | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +110 -0
new file mode 100644
index 000000000000..78a70fe04dfe
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/riscv/riscv,trace-component.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Trace Component
+
+maintainers:
+  - Anup Patel <[email protected]>
+
+description:
+  The RISC-V trace control interface specification standard memory mapped
+  components (or devices) which support both the RISC-V efficient trace
+  (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.
+  The RISC-V trace components have implementation specific directed acyclic
+  graph style interdependency where output of one component serves as input
+  to another component and certain components (such as funnel) can take inputs
+  from multiple components.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - qemu,trace-component
+      - const: riscv,trace-component
+
+  reg:
+    maxItems: 1
+
+  cpu:
+    description:
+      phandle to the cpu to which the RISC-V trace component is bound.
+    $ref: /schemas/types.yaml#/definitions/phandle
+
+  in-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Input connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port
+
+  out-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Output connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    // Example 1 (Per-hart encoder and ramsink components):
+
+    encoder@c000000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc000000 0x1000>;
+      cpu = <&CPU0>;
+      out-ports {
+        port {
+          CPU0_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU0_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c001000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc001000 0x1000>;
+      cpu = <&CPU0>;
+      in-ports {
+        port {
+          CPU0_RAMSINK_INPUT: endpoint {
+          };
+        };
+      };
+    };
+
+    encoder@c002000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc002000 0x1000>;
+      cpu = <&CPU1>;
+      out-ports {
+        port {
+          CPU1_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU1_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c003000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc003000 0x1000>;
+      cpu = <&CPU1>;
+      in-ports {
+        port {
+          CPU1_RAMSINK_INPUT: endpoint {
+          };
+        };
+      };
+    };
+
+...
Signature · 1 line
2.43.0
maintainer
#2 lore raw

Re: [PATCH 01/11] dt-bindings: Add RISC-V trace component bindings

Quoted 35 lines · Anup Patel

Add device tree bindings for the memory mapped RISC-V trace components which support both the RISC-V efficient trace (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.

The RISC-V trace components are defined by the RISC-V trace control interface specification.

---
 .../bindings/riscv/riscv,trace-component.yaml | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +16 -0
new file mode 100644
index 000000000000..78a70fe04dfe
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/riscv/riscv,trace-component.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Trace Component
+
+maintainers:
+  - Anup Patel <[email protected]>
+
+description:
+  The RISC-V trace control interface specification standard memory mapped
+  components (or devices) which support both the RISC-V efficient trace
+  (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.
+  The RISC-V trace components have implementation specific directed acyclic

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 26

Quoted 10 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +10 -0
+  graph style interdependency where output of one component serves as input
+  to another component and certain components (such as funnel) can take inputs
+  from multiple components.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - qemu,trace-component
+      - const: riscv,trace-component

Given the generic-ness of these names, I'm assuming the exact type of component is discoverable. I don't like to assume things in bindings, so spell that out.

Is the implementer discoverable? If so, you could omit the 1st compatible.

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 31

Quoted 5 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +5 -0
+
+  reg:
+    maxItems: 1
+
+  cpu:

'cpus' is the more standard property.

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 34

Quoted 3 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +3 -0
+    description:
+      phandle to the cpu to which the RISC-V trace component is bound.
+    $ref: /schemas/types.yaml#/definitions/phandle

which already has a type. So just 'maxItems: 1' here.

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 41

Quoted 7 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +7 -0
+
+  in-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Input connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port

If the N ports are N of the same data (like a mux), then fine. If each port is different, then you need to define what each port is.

Quoted 9 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +9 -0
+
+  out-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Output connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port
+
+required:

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 60

Quoted 10 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +10 -0
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    // Example 1 (Per-hart encoder and ramsink components):
+
+    encoder@c000000 {

Perhaps it is time to standardize the node names here. Perhaps 'trace'.

Quoted 53 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +50 -0
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc000000 0x1000>;
+      cpu = <&CPU0>;
+      out-ports {
+        port {
+          CPU0_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU0_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c001000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc001000 0x1000>;
+      cpu = <&CPU0>;
+      in-ports {
+        port {
+          CPU0_RAMSINK_INPUT: endpoint {
+          };
+        };
+      };
+    };
+
+    encoder@c002000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc002000 0x1000>;
+      cpu = <&CPU1>;
+      out-ports {
+        port {
+          CPU1_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU1_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c003000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc003000 0x1000>;
+      cpu = <&CPU1>;
+      in-ports {
+        port {
+          CPU1_RAMSINK_INPUT: endpoint {
+          };
+        };
+      };
+    };
+
+...
Signature · 1 line
2.43.0
author
#3 lore raw

Re: [PATCH 01/11] dt-bindings: Add RISC-V trace component bindings

Hi Rob,

Apologies for the delayed response ...

Quoted 52 lines · Rob Herring
Quoted 35 lines · Anup Patel

Add device tree bindings for the memory mapped RISC-V trace components which support both the RISC-V efficient trace (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.

The RISC-V trace components are defined by the RISC-V trace control interface specification.

---
 .../bindings/riscv/riscv,trace-component.yaml | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +16 -0
new file mode 100644
index 000000000000..78a70fe04dfe
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/riscv/riscv,trace-component.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RISC-V Trace Component
+
+maintainers:
+  - Anup Patel <[email protected]>
+
+description:
+  The RISC-V trace control interface specification standard memory mapped
+  components (or devices) which support both the RISC-V efficient trace
+  (E-trace) protocol and the RISC-V Nexus-based trace (N-trace) protocol.
+  The RISC-V trace components have implementation specific directed acyclic

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 26

Quoted 10 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +10 -0
+  graph style interdependency where output of one component serves as input
+  to another component and certain components (such as funnel) can take inputs
+  from multiple components.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - qemu,trace-component
+      - const: riscv,trace-component

Given the generic-ness of these names, I'm assuming the exact type of component is discoverable. I don't like to assume things in bindings, so spell that out.

Quoted 2 lines

Is the implementer discoverable? If so, you could omit the 1st compatible.

The component type and component version is discoverable through read-only MMIO registers but the implementer of the component needs to be inferred using implementation specific compatible string. I will add some text along these lines in the above description.

Quoted 8 lines

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 31

Quoted 6 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +5 -0
+
+  reg:
+    maxItems: 1
+
+  cpu:

'cpus' is the more standard property.

Okay, I will update.

Quoted 6 lines

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 34

Quoted 4 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +3 -0
+    description:
+      phandle to the cpu to which the RISC-V trace component is bound.
+    $ref: /schemas/types.yaml#/definitions/phandle

which already has a type. So just 'maxItems: 1' here.

Okay, I will update.

Quoted 11 lines

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 41

Quoted 8 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +7 -0
+
+  in-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Input connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port

If the N ports are N of the same data (like a mux), then fine. If each port is different, then you need to define what each port is.

Yes, the data (aka trace packets) is the same for all input trace ports even in-case of funnel (aka mux). Same thing also applies to the output ports.

Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +13 -0
+
+  out-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    patternProperties:
+      '^port(@[0-7])?$':
+        description: Output connections from RISC-V trace component
+        $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
Quoted 8 lines

Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml line 60

Quoted 6 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +6 -0
+
+examples:
+  - |
+    // Example 1 (Per-hart encoder and ramsink components):
+
+    encoder@c000000 {

Perhaps it is time to standardize the node names here. Perhaps 'trace'.

It is better to not fix the node names because this allows users to infer type of component from node name hence more human readable.

Quoted 46 lines
Quoted 46 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +45 -0
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc000000 0x1000>;
+      cpu = <&CPU0>;
+      out-ports {
+        port {
+          CPU0_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU0_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c001000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc001000 0x1000>;
+      cpu = <&CPU0>;
+      in-ports {
+        port {
+          CPU0_RAMSINK_INPUT: endpoint {
+          };
+        };
+      };
+    };
+
+    encoder@c002000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc002000 0x1000>;
+      cpu = <&CPU1>;
+      out-ports {
+        port {
+          CPU1_ENCODER_OUTPUT: endpoint {
+            remote-endpoint = <&CPU1_RAMSINK_INPUT>;
+          };
+        };
+      };
+    };
+
+    ramsink@c003000 {
+      compatible = "qemu,trace-component", "riscv,trace-component";
+      reg = <0xc003000 0x1000>;
+      cpu = <&CPU1>;
+      in-ports {
+        port {
+          CPU1_RAMSINK_INPUT: endpoint {
+          };
Quoted 8 lines
Quoted 8 lines
Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml new file +5 -1
+        };
+      };
+    };
+
+...
--

2.43.0

Regards,
Anup

author
#4 lore raw

PATCH 2/11 rvtrace: Initial implementation of driver framework

In reply to Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
 drivers/Makefile                             |   1 +
 drivers/hwtracing/Kconfig                    |   2 +
 drivers/hwtracing/rvtrace/Kconfig            |  16 +
 drivers/hwtracing/rvtrace/Makefile           |   4 +
 drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
 drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
 include/linux/rvtrace.h                      | 272 +++++++++++
 7 files changed, 953 insertions(+)
 create mode 100644 drivers/hwtracing/rvtrace/Kconfig
 create mode 100644 drivers/hwtracing/rvtrace/Makefile
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c
 create mode 100644 include/linux/rvtrace.h
drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)		+= hwtracing/coresight/
 obj-y				+= hwtracing/intel_th/
 obj-$(CONFIG_STM)		+= hwtracing/stm/
 obj-$(CONFIG_HISI_PTT)		+= hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)		+= hwtracing/rvtrace/
 obj-y				+= android/
 obj-$(CONFIG_NVMEM)		+= nvmem/
 obj-$(CONFIG_FPGA)		+= fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
 source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
 endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+	tristate "RISC-V Trace Support"
+	depends on RISCV
+	depends on OF
+	default RISCV
+	help
+	  This framework provides a kernel interface for the RISC-V trace
+	  drivers (including both e-trace and n-trace). It's intended to
+	  build a topological view of the RISC-V trace components and
+	  configure the right series of components when trace is enabled
+	  on a CPU.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0

484 lines not on this page · raw message

drivers/hwtracing/rvtrace/rvtrace-platform.c new file +174 -0

174 lines not on this page · raw message

include/linux/rvtrace.h new file +272 -0

272 lines not on this page · raw message

Signature · 1 line
2.43.0
#5 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

Quoted 638 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
  drivers/Makefile                             |   1 +
  drivers/hwtracing/Kconfig                    |   2 +
  drivers/hwtracing/rvtrace/Kconfig            |  16 +
  drivers/hwtracing/rvtrace/Makefile           |   4 +
  drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
  drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
  include/linux/rvtrace.h                      | 272 +++++++++++
  7 files changed, 953 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/Kconfig create mode 100644 drivers/hwtracing/rvtrace/Makefile create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c create mode 100644 include/linux/rvtrace.h

drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)		+= hwtracing/coresight/
  obj-y				+= hwtracing/intel_th/
  obj-$(CONFIG_STM)		+= hwtracing/stm/
  obj-$(CONFIG_HISI_PTT)		+= hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)		+= hwtracing/rvtrace/
  obj-y				+= android/
  obj-$(CONFIG_NVMEM)		+= nvmem/
  obj-$(CONFIG_FPGA)		+= fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
  source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
  endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+	tristate "RISC-V Trace Support"
+	depends on RISCV
+	depends on OF
+	default RISCV
+	help
+	  This framework provides a kernel interface for the RISC-V trace
+	  drivers (including both e-trace and n-trace). It's intended to
+	  build a topological view of the RISC-V trace components and
+	  configure the right series of components when trace is enabled
+	  on a CPU.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+	/* Lock to protect the type ID generator */
+	struct mutex lock;
+	struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+	struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+	int idx;
+
+	mutex_lock(&rvidx->lock);
+	idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+	mutex_unlock(&rvidx->lock);
+	if (idx < 0)
+		return idx;
+
+	comp->type_idx = idx;
+	return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+	struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+	mutex_lock(&rvidx->lock);
+	idr_remove(&rvidx->idr, comp->type_idx);
+	mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+	struct rvtrace_type_idx *rvidx;
+	int i;
+
+	for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+		rvidx = &rvtrace_type_idx_array[i];
+		mutex_init(&rvidx->lock);
+		idr_init(&rvidx->idr);
+	}
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+						    const struct rvtrace_component_id *ids)
+{
+	const struct rvtrace_component_id *id;
+
+	for (id = ids; id->version && id->type; id++) {
+		if (comp->id.type == id->type &&
+		    comp->id.version == id->version)
+			return id;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+	const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+	return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+	const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+	int ret = -ENODEV;
+
+	if (!rtdrv->probe)
+		return ret;
+
+	ret = rtdrv->probe(comp);
+	if (!ret)
+		comp->ready = true;
+
+	return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+	const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+	comp->ready = false;
+	if (rtdrv->remove)
+		rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+	.name	= "rvtrace",
+	.match	= rvtrace_match_device,
+	.probe	= rvtrace_probe,
+	.remove	= rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+	struct fwnode_handle *fwnode;
+	struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+	struct rvtrace_fwnode_match_data *d = data;
+
+	if (device_match_fwnode(&comp->dev, d->fwnode)) {
+		d->match = comp;
+		return 1;
+	}
+
+	return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+	int ret;
+
+	ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+		     int bit, int bitval, int timeout)
+{
+	int i = 10;
+	u32 val;
+
+	while (i--) {
+		val = rvtrace_read32(pdata, offset);
+		if (((val >> bit) & 0x1) == bitval)
+			break;
+		udelay(timeout);
+	}
+
+	return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+	rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+				comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+	val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+	rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+				comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+	if (!cpu_present(cpu))
+		return NULL;
+
+	return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+	struct rvtrace_platform_data *pdata = comp->pdata;
+	struct rvtrace_connection *conn = data;
+	int i;
+
+	if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+		for (i = 0; i < pdata->nr_inconns; i++) {
+			if (pdata->inconns[i] != conn)
+				continue;
+			pdata->inconns[i] = NULL;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+	struct rvtrace_platform_data *pdata = comp->pdata;
+	struct rvtrace_connection *conn;
+	int i;
+
+	lockdep_assert_held(&rvtrace_mutex);
+
+	for (i = 0; i < pdata->nr_outconns; i++) {
+		conn = pdata->outconns[i];
+		bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+	}
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+	struct rvtrace_platform_data *pdata = comp->pdata;
+	struct rvtrace_connection *conn = data;
+	int i;
+
+	if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+		for (i = 0; i < pdata->nr_inconns; i++) {
+			if (pdata->inconns[i])
+				continue;
+			pdata->inconns[i] = conn;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+	struct rvtrace_platform_data *pdata = comp->pdata;
+	struct rvtrace_connection *conn;
+	int i, ret;
+
+	lockdep_assert_held(&rvtrace_mutex);
+
+	for (i = 0; i < pdata->nr_outconns; i++) {
+		conn = pdata->outconns[i];
+		ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+		if (ret < 0) {
+			rvtrace_cleanup_inconns_from_outconns(comp);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+	struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+	fwnode_handle_put(comp->dev.fwnode);
+	rvtrace_free_type_idx(comp);
+	kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+	int ret;
+
+	rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+	ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+			       RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+			       pdata->control_poll_timeout_usecs);
+	if (ret)
+		return ret;
+
+	rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+			RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+				pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+	struct rvtrace_connection *conn;
+	struct rvtrace_component *comp;
+	u32 impl, type, major, minor;
+	int i, ret = 0;
+
+	if (!pdata || !pdata->dev) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	for (i = 0; i < pdata->nr_inconns; i++) {
+		if (pdata->inconns[i]) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+	}
+
+	for (i = 0; i < pdata->nr_outconns; i++) {
+		conn = pdata->outconns[i];
+		if (!conn || conn->src_port < 0 || conn->src_comp ||
+		    !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+		    conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+	}
+
+	ret = rvtrace_component_reset(pdata);
+	if (ret)
+		goto err_out;
+
+	impl = rvtrace_read32(pdata, 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;
+
+	if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+	if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+	if (!comp) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+	comp->pdata = pdata;
+	comp->id.type = type;
+	comp->id.version = rvtrace_component_mkversion(major, minor);
+	ret = rvtrace_alloc_type_idx(comp);
+	if (ret) {
+		kfree(comp);
+		goto err_out;
+	}
+
+	comp->dev.parent = pdata->dev;
+	comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+	comp->dev.release = rvtrace_component_release;
+	comp->dev.bus = &rvtrace_bustype;
+	comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+	switch (comp->id.type) {
+	case RVTRACE_COMPONENT_TYPE_ENCODER:
+		dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+		break;
+	case RVTRACE_COMPONENT_TYPE_FUNNEL:
+		dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+		break;
+	case RVTRACE_COMPONENT_TYPE_RAMSINK:
+		dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+		break;
+	case RVTRACE_COMPONENT_TYPE_PIBSINK:
+		dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+		break;
+	case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+		dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+		break;
+	default:
+		dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+		break;
+	}
+
+	mutex_lock(&rvtrace_mutex);
+
+	ret = device_register(&comp->dev);
+	if (ret) {
+		put_device(&comp->dev);
+		goto err_out_unlock;
+	}
+
+	for (i = 0; i < pdata->nr_outconns; i++) {
+		conn = pdata->outconns[i];
+		conn->src_comp = comp;
+	}
+
+	ret = rvtrace_setup_inconns_from_outconns(comp);
+	if (ret < 0) {
+		device_unregister(&comp->dev);
+		goto err_out_unlock;
+	}
+
+	if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+		rvtrace_get_component(comp);
+		per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+	}
+
+	mutex_unlock(&rvtrace_mutex);
+
+	return comp;
+
+err_out_unlock:
+	mutex_unlock(&rvtrace_mutex);
+err_out:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+	struct rvtrace_component *c;
+
+	mutex_lock(&rvtrace_mutex);
+
+	if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+		c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+		per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+		rvtrace_put_component(c);
+	}
+
+	rvtrace_cleanup_inconns_from_outconns(comp);
+	device_unregister(&comp->dev);
+
+	mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+	rtdrv->driver.owner = owner;
+	rtdrv->driver.bus = &rvtrace_bustype;
+
+	return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+	int ret;
+
+	rvtrace_init_type_idx();
+
+	ret = bus_register(&rvtrace_bustype);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&rvtrace_platform_driver);
+	if (ret) {
+		bus_unregister(&rvtrace_bustype);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+	platform_driver_unregister(&rvtrace_platform_driver);
+	bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +57 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+	struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+	struct rvtrace_connection *conn;
+	struct of_endpoint ep, rep;
+	int ret = 0, i = 0;
+
+	parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+	if (!parent)
+		return 0;
+
+	pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+	pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+				       sizeof(*pdata->outconns), GFP_KERNEL);
+	if (!pdata->outconns) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	for_each_endpoint_of_node(parent, ep_node) {
+		conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+		if (!conn) {
+			of_node_put(ep_node);
+			ret = -ENOMEM;
+			break;
+		}
+
+		ret = of_graph_parse_endpoint(ep_node, &ep);
+		if (ret) {
+			of_node_put(ep_node);
+			break;
+		}
+
+		rep_node = of_graph_get_remote_endpoint(ep_node);
+		if (!rep_node) {
+			ret = -ENODEV;
+			of_node_put(ep_node);
+			break;
+		}
+		rdev_node = of_graph_get_port_parent(rep_node);
+
+		ret = of_graph_parse_endpoint(rep_node, &rep);
+		if (ret) {

drivers/hwtracing/rvtrace/rvtrace-platform.c line 67

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +10 -0
+			of_node_put(ep_node);
+			break;
+		}
+
+		conn->src_port = ep.port;
+		conn->src_fwnode = dev_fwnode(pdata->dev);
+		/* The 'src_comp' is set by rvtrace_register_component() */
+		conn->src_comp = NULL;
+		conn->dest_port = rep.port;
+		conn->dest_fwnode = of_fwnode_handle(rdev_node);

Don't you need the fwnode_handle_get the dest_fwnode to hold the reference to the sink like coresight does?

https://elixir.bootlin.com/linux/v6.17.1/source/drivers/hwtracing/coresight/coresight-platform.c#L243

drivers/hwtracing/rvtrace/rvtrace-platform.c line 71

Quoted 4 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +4 -0
+		conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+		if (!conn->dest_comp) {
+			ret = -EPROBE_DEFER;
+			of_node_put(ep_node);

Missing a break; here.

I finally figured out the hang issue on my p550. It's caused by this missing "break;". It caused the "ep_node" to get of_node_put twice. Please check.

Quoted 370 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +102 -0
+		}
+
+		pdata->outconns[i] = conn;
+		i++;
+	}
+
+done:
+	of_node_put(parent);
+	return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+	struct device_node *parent;
+	int ret = 0;
+
+	parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+	if (!parent)
+		return 0;
+
+	pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+	pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+				      sizeof(*pdata->inconns), GFP_KERNEL);
+	if (!pdata->inconns)
+		ret = -ENOMEM;
+
+	of_node_put(parent);
+	return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+	struct rvtrace_platform_data *pdata;
+	struct device *dev = &pdev->dev;
+	struct rvtrace_component *comp;
+	struct device_node *node;
+	struct resource *res;
+	int ret;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+	pdata->dev = dev;
+	pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	pdata->io_mem = true;
+	pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!pdata->base)
+		return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+	pdata->bound_cpu = -1;
+	node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+	if (node) {
+		ret = of_cpu_node_to_id(node);
+		of_node_put(node);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+		pdata->bound_cpu = ret;
+	}
+
+	/* Default control poll timeout */
+	pdata->control_poll_timeout_usecs = 10;
+
+	ret = rvtrace_of_parse_outconns(pdata);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+	ret = rvtrace_of_parse_inconns(pdata);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+	comp = rvtrace_register_component(pdata);
+	if (IS_ERR(comp))
+		return PTR_ERR(comp);
+
+	platform_set_drvdata(pdev, comp);
+	return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+	struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+	rvtrace_unregister_component(comp);> +}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+	{ .compatible = "riscv,trace-component" },
+	{}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+	.driver = {
+		.name		= "rvtrace",
+		.of_match_table	= rvtrace_platform_match,
+	},
+	.probe = rvtrace_platform_probe,
+	.remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +262 -0
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+	RVTRACE_COMPONENT_IMPID_UNKNOWN,
+	RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+	int src_port;
+	struct fwnode_handle *src_fwnode;
+	int dest_port;
+	struct fwnode_handle *dest_fwnode;
+	struct rvtrace_component *src_comp;
+	struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+	struct device *dev;
+
+	enum rvtrace_component_impid impid;
+
+	bool io_mem;
+	union {
+		void __iomem *base;
+		struct {
+			u32 (*read)(struct rvtrace_platform_data *pdata,
+				    u32 offset, bool relaxed);
+			void (*write)(struct rvtrace_platform_data *pdata,
+				      u32 val, u32 offset, bool relaxed);
+		};
+	};
+
+	int bound_cpu;
+
+	/* Delay in microseconds when polling control register bits */
+	int control_poll_timeout_usecs;
+
+	/*
+	 * Platform driver must only populate empty pointer array without
+	 * any actual input connections.
+	 */
+	unsigned int nr_inconns;
+	struct rvtrace_connection **inconns;
+
+	/*
+	 * Platform driver must fully populate pointer array with individual
+	 * array elements pointing to actual output connections. The src_comp
+	 * of each output connection is automatically updated at the time of
+	 * registering component.
+	 */
+	unsigned int nr_outconns;
+	struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+	if (likely(pdata->io_mem))
+		return readl(pdata->base + offset);
+
+	return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+	if (likely(pdata->io_mem))
+		return readl_relaxed(pdata->base + offset);
+
+	return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+	if (likely(pdata->io_mem))
+		writel(val, pdata->base + offset);
+	else
+		pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+					   u32 val, u32 offset)
+{
+	if (likely(pdata->io_mem))
+		writel_relaxed(val, pdata->base + offset);
+	else
+		pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+	return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+	return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+	struct rvtrace_platform_data *pdata;
+	struct rvtrace_component_id id;
+	u32 type_idx;
+	struct device dev;
+	bool ready;
+};
+
+#define to_rvtrace_component(__dev)	container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+	get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+	put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+						    const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+		     int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+	const struct rvtrace_component_id *id_table;
+	int			(*probe)(struct rvtrace_component *comp);
+	void			(*remove)(struct rvtrace_component *comp);
+	struct device_driver	driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+	((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;

include/linux/rvtrace.h line 272

Quoted 10 lines
include/linux/rvtrace.h new file +10 -0
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+	if (rtdrv)
+		driver_unregister(&rtdrv->driver);
+}
+
+#endif

I suggest stress-testing it by creating some negative cases such as having dangling endpoint links in DT, e.g., RAM sinks disabled, but source encoder enabled, and check if the driver can properly handle it. Also because there can be 3 modules rvtrace/rvtrace-encoder/rvtrace-ramsink (and probably more for funnel later), check different module load orders and other edge cases such as only loading rvtrace+encoder and see if the driver can safely handle it without crash. In addition, try to load/unload the driver in a loop and see if resources are free'ed.

Bo

author
#6 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

Quoted 654 lines · Bo Gan
Quoted 638 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
  drivers/Makefile                             |   1 +
  drivers/hwtracing/Kconfig                    |   2 +
  drivers/hwtracing/rvtrace/Kconfig            |  16 +
  drivers/hwtracing/rvtrace/Makefile           |   4 +
  drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
  drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
  include/linux/rvtrace.h                      | 272 +++++++++++
  7 files changed, 953 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/Kconfig create mode 100644 drivers/hwtracing/rvtrace/Makefile create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c create mode 100644 include/linux/rvtrace.h

drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)           += hwtracing/coresight/
  obj-y                               += hwtracing/intel_th/
  obj-$(CONFIG_STM)           += hwtracing/stm/
  obj-$(CONFIG_HISI_PTT)              += hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)                += hwtracing/rvtrace/
  obj-y                               += android/
  obj-$(CONFIG_NVMEM)         += nvmem/
  obj-$(CONFIG_FPGA)          += fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
  source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
  endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+     tristate "RISC-V Trace Support"
+     depends on RISCV
+     depends on OF
+     default RISCV
+     help
+       This framework provides a kernel interface for the RISC-V trace
+       drivers (including both e-trace and n-trace). It's intended to
+       build a topological view of the RISC-V trace components and
+       configure the right series of components when trace is enabled
+       on a CPU.
+
+       To compile this driver as a module, choose M here: the module
+       will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+     /* Lock to protect the type ID generator */
+     struct mutex lock;
+     struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+     struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+     int idx;
+
+     mutex_lock(&rvidx->lock);
+     idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+     mutex_unlock(&rvidx->lock);
+     if (idx < 0)
+             return idx;
+
+     comp->type_idx = idx;
+     return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+     struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+     mutex_lock(&rvidx->lock);
+     idr_remove(&rvidx->idr, comp->type_idx);
+     mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+     struct rvtrace_type_idx *rvidx;
+     int i;
+
+     for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+             rvidx = &rvtrace_type_idx_array[i];
+             mutex_init(&rvidx->lock);
+             idr_init(&rvidx->idr);
+     }
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                 const struct rvtrace_component_id *ids)
+{
+     const struct rvtrace_component_id *id;
+
+     for (id = ids; id->version && id->type; id++) {
+             if (comp->id.type == id->type &&
+                 comp->id.version == id->version)
+                     return id;
+     }
+
+     return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+     const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+     return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+     const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+     int ret = -ENODEV;
+
+     if (!rtdrv->probe)
+             return ret;
+
+     ret = rtdrv->probe(comp);
+     if (!ret)
+             comp->ready = true;
+
+     return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+     const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+     comp->ready = false;
+     if (rtdrv->remove)
+             rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+     .name   = "rvtrace",
+     .match  = rvtrace_match_device,
+     .probe  = rvtrace_probe,
+     .remove = rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+     struct fwnode_handle *fwnode;
+     struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+     struct rvtrace_fwnode_match_data *d = data;
+
+     if (device_match_fwnode(&comp->dev, d->fwnode)) {
+             d->match = comp;
+             return 1;
+     }
+
+     return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+     struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+     int ret;
+
+     ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+     if (ret < 0)
+             return ERR_PTR(ret);
+
+     return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                  int bit, int bitval, int timeout)
+{
+     int i = 10;
+     u32 val;
+
+     while (i--) {
+             val = rvtrace_read32(pdata, offset);
+             if (((val >> bit) & 0x1) == bitval)
+                     break;
+             udelay(timeout);
+     }
+
+     return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+     u32 val;
+
+     val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+     val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+     rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+     return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                             RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+                             comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+     u32 val;
+
+     val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+     val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+     rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+     return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                             RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+                             comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+     if (!cpu_present(cpu))
+             return NULL;
+
+     return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+     struct rvtrace_platform_data *pdata = comp->pdata;
+     struct rvtrace_connection *conn = data;
+     int i;
+
+     if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+             for (i = 0; i < pdata->nr_inconns; i++) {
+                     if (pdata->inconns[i] != conn)
+                             continue;
+                     pdata->inconns[i] = NULL;
+                     return 1;
+             }
+     }
+
+     return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+     struct rvtrace_platform_data *pdata = comp->pdata;
+     struct rvtrace_connection *conn;
+     int i;
+
+     lockdep_assert_held(&rvtrace_mutex);
+
+     for (i = 0; i < pdata->nr_outconns; i++) {
+             conn = pdata->outconns[i];
+             bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+     }
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+     struct rvtrace_platform_data *pdata = comp->pdata;
+     struct rvtrace_connection *conn = data;
+     int i;
+
+     if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+             for (i = 0; i < pdata->nr_inconns; i++) {
+                     if (pdata->inconns[i])
+                             continue;
+                     pdata->inconns[i] = conn;
+                     return 1;
+             }
+     }
+
+     return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+     struct rvtrace_platform_data *pdata = comp->pdata;
+     struct rvtrace_connection *conn;
+     int i, ret;
+
+     lockdep_assert_held(&rvtrace_mutex);
+
+     for (i = 0; i < pdata->nr_outconns; i++) {
+             conn = pdata->outconns[i];
+             ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+             if (ret < 0) {
+                     rvtrace_cleanup_inconns_from_outconns(comp);
+                     return ret;
+             }
+     }
+
+     return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+     struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+     fwnode_handle_put(comp->dev.fwnode);
+     rvtrace_free_type_idx(comp);
+     kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+     int ret;
+
+     rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+     ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                            RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+                            pdata->control_poll_timeout_usecs);
+     if (ret)
+             return ret;
+
+     rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+                     RVTRACE_COMPONENT_CTRL_OFFSET);
+     return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                             RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+                             pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+     struct rvtrace_connection *conn;
+     struct rvtrace_component *comp;
+     u32 impl, type, major, minor;
+     int i, ret = 0;
+
+     if (!pdata || !pdata->dev) {
+             ret = -EINVAL;
+             goto err_out;
+     }
+
+     for (i = 0; i < pdata->nr_inconns; i++) {
+             if (pdata->inconns[i]) {
+                     ret = -EINVAL;
+                     goto err_out;
+             }
+     }
+
+     for (i = 0; i < pdata->nr_outconns; i++) {
+             conn = pdata->outconns[i];
+             if (!conn || conn->src_port < 0 || conn->src_comp ||
+                 !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+                 conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+                     ret = -EINVAL;
+                     goto err_out;
+             }
+     }
+
+     ret = rvtrace_component_reset(pdata);
+     if (ret)
+             goto err_out;
+
+     impl = rvtrace_read32(pdata, 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;
+
+     if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+             ret = -EINVAL;
+             goto err_out;
+     }
+     if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+             ret = -EINVAL;
+             goto err_out;
+     }
+
+     comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+     if (!comp) {
+             ret = -ENOMEM;
+             goto err_out;
+     }
+     comp->pdata = pdata;
+     comp->id.type = type;
+     comp->id.version = rvtrace_component_mkversion(major, minor);
+     ret = rvtrace_alloc_type_idx(comp);
+     if (ret) {
+             kfree(comp);
+             goto err_out;
+     }
+
+     comp->dev.parent = pdata->dev;
+     comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+     comp->dev.release = rvtrace_component_release;
+     comp->dev.bus = &rvtrace_bustype;
+     comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+     switch (comp->id.type) {
+     case RVTRACE_COMPONENT_TYPE_ENCODER:
+             dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+             break;
+     case RVTRACE_COMPONENT_TYPE_FUNNEL:
+             dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+             break;
+     case RVTRACE_COMPONENT_TYPE_RAMSINK:
+             dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+             break;
+     case RVTRACE_COMPONENT_TYPE_PIBSINK:
+             dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+             break;
+     case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+             dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+             break;
+     default:
+             dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+             break;
+     }
+
+     mutex_lock(&rvtrace_mutex);
+
+     ret = device_register(&comp->dev);
+     if (ret) {
+             put_device(&comp->dev);
+             goto err_out_unlock;
+     }
+
+     for (i = 0; i < pdata->nr_outconns; i++) {
+             conn = pdata->outconns[i];
+             conn->src_comp = comp;
+     }
+
+     ret = rvtrace_setup_inconns_from_outconns(comp);
+     if (ret < 0) {
+             device_unregister(&comp->dev);
+             goto err_out_unlock;
+     }
+
+     if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+             rvtrace_get_component(comp);
+             per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+     }
+
+     mutex_unlock(&rvtrace_mutex);
+
+     return comp;
+
+err_out_unlock:
+     mutex_unlock(&rvtrace_mutex);
+err_out:
+     return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+     struct rvtrace_component *c;
+
+     mutex_lock(&rvtrace_mutex);
+
+     if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+             c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+             per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+             rvtrace_put_component(c);
+     }
+
+     rvtrace_cleanup_inconns_from_outconns(comp);
+     device_unregister(&comp->dev);
+
+     mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+     rtdrv->driver.owner = owner;
+     rtdrv->driver.bus = &rvtrace_bustype;
+
+     return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+     int ret;
+
+     rvtrace_init_type_idx();
+
+     ret = bus_register(&rvtrace_bustype);
+     if (ret)
+             return ret;
+
+     ret = platform_driver_register(&rvtrace_platform_driver);
+     if (ret) {
+             bus_unregister(&rvtrace_bustype);
+             return ret;
+     }
+
+     return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+     platform_driver_unregister(&rvtrace_platform_driver);
+     bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +57 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+     struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+     struct rvtrace_connection *conn;
+     struct of_endpoint ep, rep;
+     int ret = 0, i = 0;
+
+     parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+     if (!parent)
+             return 0;
+
+     pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+     pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+                                    sizeof(*pdata->outconns), GFP_KERNEL);
+     if (!pdata->outconns) {
+             ret = -ENOMEM;
+             goto done;
+     }
+
+     for_each_endpoint_of_node(parent, ep_node) {
+             conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+             if (!conn) {
+                     of_node_put(ep_node);
+                     ret = -ENOMEM;
+                     break;
+             }
+
+             ret = of_graph_parse_endpoint(ep_node, &ep);
+             if (ret) {
+                     of_node_put(ep_node);
+                     break;
+             }
+
+             rep_node = of_graph_get_remote_endpoint(ep_node);
+             if (!rep_node) {
+                     ret = -ENODEV;
+                     of_node_put(ep_node);
+                     break;
+             }
+             rdev_node = of_graph_get_port_parent(rep_node);
+
+             ret = of_graph_parse_endpoint(rep_node, &rep);
+             if (ret) {

drivers/hwtracing/rvtrace/rvtrace-platform.c line 67

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +10 -0
+                     of_node_put(ep_node);
+                     break;
+             }
+
+             conn->src_port = ep.port;
+             conn->src_fwnode = dev_fwnode(pdata->dev);
+             /* The 'src_comp' is set by rvtrace_register_component() */
+             conn->src_comp = NULL;
+             conn->dest_port = rep.port;
+             conn->dest_fwnode = of_fwnode_handle(rdev_node);

Don't you need the fwnode_handle_get the dest_fwnode to hold the reference to the sink like coresight does?

Quoted 1 line

https://elixir.bootlin.com/linux/v6.17.1/source/drivers/hwtracing/coresight/coresight-platform.c#L243

Okay, I will update.

Quoted 10 lines

drivers/hwtracing/rvtrace/rvtrace-platform.c line 71

Quoted 5 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +4 -0
+             conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+             if (!conn->dest_comp) {
+                     ret = -EPROBE_DEFER;
+                     of_node_put(ep_node);

Missing a break; here.

I finally figured out the hang issue on my p550. It's caused by this missing "break;". It caused the "ep_node" to get of_node_put twice. Please check.

Good catch. Thanks for reporting.

Quoted 381 lines
Quoted 371 lines
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +102 -0
+             }
+
+             pdata->outconns[i] = conn;
+             i++;
+     }
+
+done:
+     of_node_put(parent);
+     return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+     struct device_node *parent;
+     int ret = 0;
+
+     parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+     if (!parent)
+             return 0;
+
+     pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+     pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+                                   sizeof(*pdata->inconns), GFP_KERNEL);
+     if (!pdata->inconns)
+             ret = -ENOMEM;
+
+     of_node_put(parent);
+     return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+     struct rvtrace_platform_data *pdata;
+     struct device *dev = &pdev->dev;
+     struct rvtrace_component *comp;
+     struct device_node *node;
+     struct resource *res;
+     int ret;
+
+     pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+     if (!pdata)
+             return -ENOMEM;
+     pdata->dev = dev;
+     pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     if (!res)
+             return -EINVAL;
+
+     pdata->io_mem = true;
+     pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+     if (!pdata->base)
+             return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+     pdata->bound_cpu = -1;
+     node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+     if (node) {
+             ret = of_cpu_node_to_id(node);
+             of_node_put(node);
+             if (ret < 0)
+                     return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+             pdata->bound_cpu = ret;
+     }
+
+     /* Default control poll timeout */
+     pdata->control_poll_timeout_usecs = 10;
+
+     ret = rvtrace_of_parse_outconns(pdata);
+     if (ret)
+             return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+     ret = rvtrace_of_parse_inconns(pdata);
+     if (ret)
+             return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+     comp = rvtrace_register_component(pdata);
+     if (IS_ERR(comp))
+             return PTR_ERR(comp);
+
+     platform_set_drvdata(pdev, comp);
+     return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+     struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+     rvtrace_unregister_component(comp);> +}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+     { .compatible = "riscv,trace-component" },
+     {}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+     .driver = {
+             .name           = "rvtrace",
+             .of_match_table = rvtrace_platform_match,
+     },
+     .probe = rvtrace_platform_probe,
+     .remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +262 -0
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+     RVTRACE_COMPONENT_IMPID_UNKNOWN,
+     RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+     int src_port;
+     struct fwnode_handle *src_fwnode;
+     int dest_port;
+     struct fwnode_handle *dest_fwnode;
+     struct rvtrace_component *src_comp;
+     struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+     struct device *dev;
+
+     enum rvtrace_component_impid impid;
+
+     bool io_mem;
+     union {
+             void __iomem *base;
+             struct {
+                     u32 (*read)(struct rvtrace_platform_data *pdata,
+                                 u32 offset, bool relaxed);
+                     void (*write)(struct rvtrace_platform_data *pdata,
+                                   u32 val, u32 offset, bool relaxed);
+             };
+     };
+
+     int bound_cpu;
+
+     /* Delay in microseconds when polling control register bits */
+     int control_poll_timeout_usecs;
+
+     /*
+      * Platform driver must only populate empty pointer array without
+      * any actual input connections.
+      */
+     unsigned int nr_inconns;
+     struct rvtrace_connection **inconns;
+
+     /*
+      * Platform driver must fully populate pointer array with individual
+      * array elements pointing to actual output connections. The src_comp
+      * of each output connection is automatically updated at the time of
+      * registering component.
+      */
+     unsigned int nr_outconns;
+     struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+     if (likely(pdata->io_mem))
+             return readl(pdata->base + offset);
+
+     return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+     if (likely(pdata->io_mem))
+             return readl_relaxed(pdata->base + offset);
+
+     return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+     if (likely(pdata->io_mem))
+             writel(val, pdata->base + offset);
+     else
+             pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+                                        u32 val, u32 offset)
+{
+     if (likely(pdata->io_mem))
+             writel_relaxed(val, pdata->base + offset);
+     else
+             pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+     return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+     return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+     struct rvtrace_platform_data *pdata;
+     struct rvtrace_component_id id;
+     u32 type_idx;
+     struct device dev;
+     bool ready;
+};
+
+#define to_rvtrace_component(__dev)  container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+     get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+     put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                 const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                  int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+     const struct rvtrace_component_id *id_table;
+     int                     (*probe)(struct rvtrace_component *comp);
+     void                    (*remove)(struct rvtrace_component *comp);
+     struct device_driver    driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+     ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;

include/linux/rvtrace.h line 272

Quoted 10 lines
include/linux/rvtrace.h new file +10 -0
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+     if (rtdrv)
+             driver_unregister(&rtdrv->driver);
+}
+
+#endif
Quoted 8 lines

I suggest stress-testing it by creating some negative cases such as having dangling endpoint links in DT, e.g., RAM sinks disabled, but source encoder enabled, and check if the driver can properly handle it. Also because there can be 3 modules rvtrace/rvtrace-encoder/rvtrace-ramsink (and probably more for funnel later), check different module load orders and other edge cases such as only loading rvtrace+encoder and see if the driver can safely handle it without crash. In addition, try to load/unload the driver in a loop and see if resources are free'ed.

Testing infrastructure will develop over-time and there are a lot of topology configurations to cover.

The current goal is to add a reasonably flexible rvtrace framework which can be extended and hardened over time.

Regards,
Anup

#7 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

In reply to Anup Patel

Hi Anup,

Thank you for providing the RVtrace infrastructure in Linux. It is great to see support for manipulating RISC-V trace components via the Linux perf tool.

SiFive provides a similar instruction trace specification that allows users to collect processor execution history and other events on SiFive platforms. This specification is implemented on the HiFive Premier P550 board. The specification is available here:
https://lists.riscv.org/g/tech-nexus/files/RISC-V-Trace-Control-Interface-Proposed-20200612.pdf

The fundamental concepts behind both specifications are aligned:
1. Both instruction trace systems are composed of Trace Encoders, Trace Funnels, and Trace Sinks.
a. Trace encoder: Each hart has its own trace encoder. It monitors
the ingress port to record addresses where program flow discontinuities occur. It compresses the log using either the Efficient Trace for RISC-V or RISC-V N-Trace specification (SiFive encoders support Nexus-based trace only) and transmits it to a downstream Trace Funnel or Sink.
b. Trace funnel: Functionally identical in both systems, the funnel
aggregates traces from multiple inputs (encoders or other funnels) and forwards the combined stream to a designated sink.
c. Trace sink: Both specifications support SRAM, System Memory, PIB,
and ATB Bridge sinks.

2. The control flow for starting and stopping recording is also consistent:
a. Start tracing: Proceed from the downstream sink up to the trace
encoder to ensure sinks are ready to capture logs.
b. Stop tracing: Proceed from the trace encoder down to the sink to
ensure all logs are flushed.

However, these two specifications have some differences in implementation, primarily in:
1. MMIO Register Definitions: While functionally similar, the register layouts for SiFive encoders and funnels differ from the RISC-V standard.
2. Lack of Independent Sink Components: In the SiFive implementation, sink configuration registers are embedded within the trace encoder and funnel MMIO regions, rather than existing as standalone components.
3. Trace Path configuration:
a. RISC-V trace component is allowed to pass down the trace log to a
single or multiple next-level trace components, depending on the HW capabilities and connection. This trace component cannot configure the output target via the MMIO register, but the next-level trace component has the ability to disable the inputs from a specific trace component.
b. The SiFive trace component can only output the trace log to a
designated sink, which is specified in the corresponding MMIO register. The designated sink cannot be configured to disable any input.
4. SiFive customer feature: SiFive platforms include custom event collection capabilities that require a dedicated PMU registration to expose via the Linux perf tool.

Given the similarities in control flow, we propose integrating the SiFive trace driver into your RVtrace infrastructure to avoid code duplication. Below is our proposed integration approach:

1. DT File (Device Tree)
- Inherit the current dt-bindings for RISC-V trace components, specifically the in-ports and out-ports descriptions for topology.
- Add "sifive,trace-component" to the compatible string of SiFive nodes.
- Add a new DT property to indicate the specific type of SiFive trace component. This is necessary because the corresponding register in the SiFive specification does not expose the component's type (Encoder/Funnel) to software.
2. Perf Tool
- Register a new perf PMU specifically for SiFive trace. This also allows us to define custom event configurations for SiFive-specific features.
- The Perf tool will support both PMUs simultaneously. The final selected operating PMU depends on the PMU name in the perf command and the platform capability.
- Leverage the callback functions registered to "struct auxtrace_record" as much as possible.
3. Linux Device Driver
- Device Probe:
- Add "sifive,trace-component" to rvtrace_platform_match[] to enable rvtrace_platform_probe() to support SiFive trace components.
- Update rvtrace_register_component() to support registering SiFive encoders and funnels to the rvtrace bus.
- Plan to let the SiFive trace encoder and funnel share the same type ID as RISC-V. In this condition, to enable the driver to distinguish between SiFive and RISC-V hardware, it would be necessary to add a new vendor ID field into
"struct rvtrace_component_id".
- Driver Implementations:
- Like other drivers of RISC-V trace components, the SiFive encoder and funnel driver will implement the required functions in the rvtrace_driver.
- Trace Delivery Path (Encoder to Sink):
- This infrastructure can be used in the same way to establish the topology of the entire trace system via the DT file.
- Based on the same topology descriptions, the trace delivery path can use the same logic to create.
- The primary difference lies in the trace path configuration. To address this, we propose delegating the path configuration logic to the .start() and .stop() callbacks in "struct rvtrace_driver". This allows the routing implementation to be encapsulated within the specific driver. To support this, we would need to update the definition of the .start() and .stop() callbacks to accept the adjacent node information as arguments, allowing the driver to perform the necessary MMIO writes for routing.

- Sink Manipulation:
- Standard RISC-V: Managed through dedicated drivers (init during probe, access via callbacks during dump).
- SiFive Implementation: Sink regs are embedded in Encoder/Funnel MMIO.
- We propose that the SiFive encoder and funnel drivers:
a. Initialize their supported sinks during the driver probe
sequence.
b. Implement and register the appropriate sink access callbacks,
ensuring they align with the standard perf log dumping workflow.

Do you think the proposed solution is feasible? I would appreciate your feedback.

Best regards,
Vincent

Quoted 1,042 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
 drivers/Makefile                             |   1 +
 drivers/hwtracing/Kconfig                    |   2 +
 drivers/hwtracing/rvtrace/Kconfig            |  16 +
 drivers/hwtracing/rvtrace/Makefile           |   4 +
 drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
 drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
 include/linux/rvtrace.h                      | 272 +++++++++++
 7 files changed, 953 insertions(+)
 create mode 100644 drivers/hwtracing/rvtrace/Kconfig
 create mode 100644 drivers/hwtracing/rvtrace/Makefile
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c
 create mode 100644 include/linux/rvtrace.h
drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)             += hwtracing/coresight/
 obj-y                          += hwtracing/intel_th/
 obj-$(CONFIG_STM)              += hwtracing/stm/
 obj-$(CONFIG_HISI_PTT)         += hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)          += hwtracing/rvtrace/
 obj-y                          += android/
 obj-$(CONFIG_NVMEM)            += nvmem/
 obj-$(CONFIG_FPGA)             += fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
 source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
 endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+       tristate "RISC-V Trace Support"
+       depends on RISCV
+       depends on OF
+       default RISCV
+       help
+         This framework provides a kernel interface for the RISC-V trace
+         drivers (including both e-trace and n-trace). It's intended to
+         build a topological view of the RISC-V trace components and
+         configure the right series of components when trace is enabled
+         on a CPU.
+
+         To compile this driver as a module, choose M here: the module
+         will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+       /* Lock to protect the type ID generator */
+       struct mutex lock;
+       struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+       int idx;
+
+       mutex_lock(&rvidx->lock);
+       idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+       mutex_unlock(&rvidx->lock);
+       if (idx < 0)
+               return idx;
+
+       comp->type_idx = idx;
+       return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+       mutex_lock(&rvidx->lock);
+       idr_remove(&rvidx->idr, comp->type_idx);
+       mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+       struct rvtrace_type_idx *rvidx;
+       int i;
+
+       for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+               rvidx = &rvtrace_type_idx_array[i];
+               mutex_init(&rvidx->lock);
+               idr_init(&rvidx->idr);
+       }
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids)
+{
+       const struct rvtrace_component_id *id;
+
+       for (id = ids; id->version && id->type; id++) {
+               if (comp->id.type == id->type &&
+                   comp->id.version == id->version)
+                       return id;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       int ret = -ENODEV;
+
+       if (!rtdrv->probe)
+               return ret;
+
+       ret = rtdrv->probe(comp);
+       if (!ret)
+               comp->ready = true;
+
+       return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       comp->ready = false;
+       if (rtdrv->remove)
+               rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+       .name   = "rvtrace",
+       .match  = rvtrace_match_device,
+       .probe  = rvtrace_probe,
+       .remove = rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+       struct fwnode_handle *fwnode;
+       struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_fwnode_match_data *d = data;
+
+       if (device_match_fwnode(&comp->dev, d->fwnode)) {
+               d->match = comp;
+               return 1;
+       }
+
+       return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+       struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+       int ret;
+
+       ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout)
+{
+       int i = 10;
+       u32 val;
+
+       while (i--) {
+               val = rvtrace_read32(pdata, offset);
+               if (((val >> bit) & 0x1) == bitval)
+                       break;
+               udelay(timeout);
+       }
+
+       return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+       if (!cpu_present(cpu))
+               return NULL;
+
+       return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i] != conn)
+                               continue;
+                       pdata->inconns[i] = NULL;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+       }
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i])
+                               continue;
+                       pdata->inconns[i] = conn;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i, ret;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+               if (ret < 0) {
+                       rvtrace_cleanup_inconns_from_outconns(comp);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       fwnode_handle_put(comp->dev.fwnode);
+       rvtrace_free_type_idx(comp);
+       kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+       int ret;
+
+       rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+       ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                              RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+                              pdata->control_poll_timeout_usecs);
+       if (ret)
+               return ret;
+
+       rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+                       RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+                               pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+       struct rvtrace_connection *conn;
+       struct rvtrace_component *comp;
+       u32 impl, type, major, minor;
+       int i, ret = 0;
+
+       if (!pdata || !pdata->dev) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       for (i = 0; i < pdata->nr_inconns; i++) {
+               if (pdata->inconns[i]) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               if (!conn || conn->src_port < 0 || conn->src_comp ||
+                   !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+                   conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       ret = rvtrace_component_reset(pdata);
+       if (ret)
+               goto err_out;
+
+       impl = rvtrace_read32(pdata, 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;
+
+       if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+       if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+       if (!comp) {
+               ret = -ENOMEM;
+               goto err_out;
+       }
+       comp->pdata = pdata;
+       comp->id.type = type;
+       comp->id.version = rvtrace_component_mkversion(major, minor);
+       ret = rvtrace_alloc_type_idx(comp);
+       if (ret) {
+               kfree(comp);
+               goto err_out;
+       }
+
+       comp->dev.parent = pdata->dev;
+       comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+       comp->dev.release = rvtrace_component_release;
+       comp->dev.bus = &rvtrace_bustype;
+       comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+       switch (comp->id.type) {
+       case RVTRACE_COMPONENT_TYPE_ENCODER:
+               dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_FUNNEL:
+               dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_RAMSINK:
+               dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_PIBSINK:
+               dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+               dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+               break;
+       default:
+               dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+               break;
+       }
+
+       mutex_lock(&rvtrace_mutex);
+
+       ret = device_register(&comp->dev);
+       if (ret) {
+               put_device(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               conn->src_comp = comp;
+       }
+
+       ret = rvtrace_setup_inconns_from_outconns(comp);
+       if (ret < 0) {
+               device_unregister(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               rvtrace_get_component(comp);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+       }
+
+       mutex_unlock(&rvtrace_mutex);
+
+       return comp;
+
+err_out_unlock:
+       mutex_unlock(&rvtrace_mutex);
+err_out:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+       struct rvtrace_component *c;
+
+       mutex_lock(&rvtrace_mutex);
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+               rvtrace_put_component(c);
+       }
+
+       rvtrace_cleanup_inconns_from_outconns(comp);
+       device_unregister(&comp->dev);
+
+       mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+       rtdrv->driver.owner = owner;
+       rtdrv->driver.bus = &rvtrace_bustype;
+
+       return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+       int ret;
+
+       rvtrace_init_type_idx();
+
+       ret = bus_register(&rvtrace_bustype);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&rvtrace_platform_driver);
+       if (ret) {
+               bus_unregister(&rvtrace_bustype);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+       platform_driver_unregister(&rvtrace_platform_driver);
+       bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +174 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+       struct rvtrace_connection *conn;
+       struct of_endpoint ep, rep;
+       int ret = 0, i = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+       pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+                                      sizeof(*pdata->outconns), GFP_KERNEL);
+       if (!pdata->outconns) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       for_each_endpoint_of_node(parent, ep_node) {
+               conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+               if (!conn) {
+                       of_node_put(ep_node);
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = of_graph_parse_endpoint(ep_node, &ep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               rep_node = of_graph_get_remote_endpoint(ep_node);
+               if (!rep_node) {
+                       ret = -ENODEV;
+                       of_node_put(ep_node);
+                       break;
+               }
+               rdev_node = of_graph_get_port_parent(rep_node);
+
+               ret = of_graph_parse_endpoint(rep_node, &rep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               conn->src_port = ep.port;
+               conn->src_fwnode = dev_fwnode(pdata->dev);
+               /* The 'src_comp' is set by rvtrace_register_component() */
+               conn->src_comp = NULL;
+               conn->dest_port = rep.port;
+               conn->dest_fwnode = of_fwnode_handle(rdev_node);
+               conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+               if (!conn->dest_comp) {
+                       ret = -EPROBE_DEFER;
+                       of_node_put(ep_node);
+               }
+
+               pdata->outconns[i] = conn;
+               i++;
+       }
+
+done:
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent;
+       int ret = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+       pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+                                     sizeof(*pdata->inconns), GFP_KERNEL);
+       if (!pdata->inconns)
+               ret = -ENOMEM;
+
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+       struct rvtrace_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct rvtrace_component *comp;
+       struct device_node *node;
+       struct resource *res;
+       int ret;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+       pdata->dev = dev;
+       pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       pdata->io_mem = true;
+       pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!pdata->base)
+               return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+       pdata->bound_cpu = -1;
+       node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+       if (node) {
+               ret = of_cpu_node_to_id(node);
+               of_node_put(node);
+               if (ret < 0)
+                       return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+               pdata->bound_cpu = ret;
+       }
+
+       /* Default control poll timeout */
+       pdata->control_poll_timeout_usecs = 10;
+
+       ret = rvtrace_of_parse_outconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+       ret = rvtrace_of_parse_inconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+       comp = rvtrace_register_component(pdata);
+       if (IS_ERR(comp))
+               return PTR_ERR(comp);
+
+       platform_set_drvdata(pdev, comp);
+       return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+       struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+       rvtrace_unregister_component(comp);
+}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+       { .compatible = "riscv,trace-component" },
+       {}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+       .driver = {
+               .name           = "rvtrace",
+               .of_match_table = rvtrace_platform_match,
+       },
+       .probe = rvtrace_platform_probe,
+       .remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +272 -1
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+       RVTRACE_COMPONENT_IMPID_UNKNOWN,
+       RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+       int src_port;
+       struct fwnode_handle *src_fwnode;
+       int dest_port;
+       struct fwnode_handle *dest_fwnode;
+       struct rvtrace_component *src_comp;
+       struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+       struct device *dev;
+
+       enum rvtrace_component_impid impid;
+
+       bool io_mem;
+       union {
+               void __iomem *base;
+               struct {
+                       u32 (*read)(struct rvtrace_platform_data *pdata,
+                                   u32 offset, bool relaxed);
+                       void (*write)(struct rvtrace_platform_data *pdata,
+                                     u32 val, u32 offset, bool relaxed);
+               };
+       };
+
+       int bound_cpu;
+
+       /* Delay in microseconds when polling control register bits */
+       int control_poll_timeout_usecs;
+
+       /*
+        * Platform driver must only populate empty pointer array without
+        * any actual input connections.
+        */
+       unsigned int nr_inconns;
+       struct rvtrace_connection **inconns;
+
+       /*
+        * Platform driver must fully populate pointer array with individual
+        * array elements pointing to actual output connections. The src_comp
+        * of each output connection is automatically updated at the time of
+        * registering component.
+        */
+       unsigned int nr_outconns;
+       struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl(pdata->base + offset);
+
+       return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl_relaxed(pdata->base + offset);
+
+       return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+                                          u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel_relaxed(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+       struct rvtrace_platform_data *pdata;
+       struct rvtrace_component_id id;
+       u32 type_idx;
+       struct device dev;
+       bool ready;
+};
+
+#define to_rvtrace_component(__dev)    container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+       get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+       put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+       const struct rvtrace_component_id *id_table;
+       int                     (*probe)(struct rvtrace_component *comp);
+       void                    (*remove)(struct rvtrace_component *comp);
+       struct device_driver    driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+       ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+       if (rtdrv)
+               driver_unregister(&rtdrv->driver);
+}
+
+#endif
--

2.43.0

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

#8 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

Quoted 28 lines · Vincent Chen

Hi Anup,

Thank you for providing the RVtrace infrastructure in Linux. It is great to see support for manipulating RISC-V trace components via the Linux perf tool.

SiFive provides a similar instruction trace specification that allows users to collect processor execution history and other events on SiFive platforms. This specification is implemented on the HiFive Premier P550 board. The specification is available here:
https://lists.riscv.org/g/tech-nexus/files/RISC-V-Trace-Control-Interface-Proposed-20200612.pdf

The fundamental concepts behind both specifications are aligned:
1. Both instruction trace systems are composed of Trace Encoders, Trace Funnels, and Trace Sinks.
a. Trace encoder: Each hart has its own trace encoder. It monitors
the ingress port to record addresses where program flow discontinuities occur. It compresses the log using either the Efficient Trace for RISC-V or RISC-V N-Trace specification (SiFive encoders support Nexus-based trace only) and transmits it to a downstream Trace Funnel or Sink.
b. Trace funnel: Functionally identical in both systems, the funnel
aggregates traces from multiple inputs (encoders or other funnels) and forwards the combined stream to a designated sink.
c. Trace sink: Both specifications support SRAM, System Memory, PIB,
and ATB Bridge sinks.

Quoted 5 lines

2. The control flow for starting and stopping recording is also consistent:
a. Start tracing: Proceed from the downstream sink up to the trace
encoder to ensure sinks are ready to capture logs.
b. Stop tracing: Proceed from the trace encoder down to the sink to
ensure all logs are flushed.

It's good that at high-level the SiFive trace is close to the ratified RISC-V trace so we won't have major re-work in the rvtrace framework.

Quoted 6 lines

However, these two specifications have some differences in implementation, primarily in:
1. MMIO Register Definitions: While functionally similar, the register layouts for SiFive encoders and funnels differ from the RISC-V standard.

The rvtrace framework (particularly rvtrace_register_component()) implicitly accesses few registers assuming components are RISC-V trace compliant which will break for SiFive trace components. I will try to make the rvtrace framework a little more generic in the next revision.

Quoted 3 lines

2. Lack of Independent Sink Components: In the SiFive implementation, sink configuration registers are embedded within the trace encoder and funnel MMIO regions, rather than existing as standalone components.

This is going to be the case for the self-hosted trace as well so this aspect needs to be addressed anyway in the future.

Quoted 19 lines

3. Trace Path configuration:
a. RISC-V trace component is allowed to pass down the trace log to a
single or multiple next-level trace components, depending on the HW capabilities and connection. This trace component cannot configure the output target via the MMIO register, but the next-level trace component has the ability to disable the inputs from a specific trace component.
b. The SiFive trace component can only output the trace log to a
designated sink, which is specified in the corresponding MMIO register. The designated sink cannot be configured to disable any input.
4. SiFive customer feature: SiFive platforms include custom event collection capabilities that require a dedicated PMU registration to expose via the Linux perf tool.

Given the similarities in control flow, we propose integrating the SiFive trace driver into your RVtrace infrastructure to avoid code duplication. Below is our proposed integration approach:

Quoted 5 lines

1. DT File (Device Tree)
- Inherit the current dt-bindings for RISC-V trace components, specifically the in-ports and out-ports descriptions for topology.
- Add "sifive,trace-component" to the compatible string of SiFive nodes.

New compatible string is mandatory because the SiFive trace component is not directly compatible with the RISC-V trace component.

Quoted 4 lines

- Add a new DT property to indicate the specific type of SiFive trace component. This is necessary because the corresponding register in the SiFive specification does not expose the component's type (Encoder/Funnel) to software.

Rather than new DT property, I would suggest using separate compatible strings (such as sifive,trace-encoder and sifive,trace-funnel or something similar).

Quoted 9 lines

2. Perf Tool
- Register a new perf PMU specifically for SiFive trace. This also allows us to define custom event configurations for SiFive-specific features.
- The Perf tool will support both PMUs simultaneously. The final selected operating PMU depends on the PMU name in the perf command and the platform capability.
- Leverage the callback functions registered to "struct auxtrace_record" as much as possible.

IMO, a separate perf PMU instance for SiFive should be the last option. This certainly needs more brainstorming in the RISE kernel working group.

Quoted 42 lines

3. Linux Device Driver
- Device Probe:
- Add "sifive,trace-component" to rvtrace_platform_match[] to enable rvtrace_platform_probe() to support SiFive trace components.
- Update rvtrace_register_component() to support registering SiFive encoders and funnels to the rvtrace bus.
- Plan to let the SiFive trace encoder and funnel share the same type ID as RISC-V. In this condition, to enable the driver to distinguish between SiFive and RISC-V hardware, it would be necessary to add a new vendor ID field into
"struct rvtrace_component_id".
- Driver Implementations:
- Like other drivers of RISC-V trace components, the SiFive encoder and funnel driver will implement the required functions in the rvtrace_driver.
- Trace Delivery Path (Encoder to Sink):
- This infrastructure can be used in the same way to establish the topology of the entire trace system via the DT file.
- Based on the same topology descriptions, the trace delivery path can use the same logic to create.
- The primary difference lies in the trace path configuration. To address this, we propose delegating the path configuration logic to the .start() and .stop() callbacks in "struct rvtrace_driver". This allows the routing implementation to be encapsulated within the specific driver. To support this, we would need to update the definition of the .start() and .stop() callbacks to accept the adjacent node information as arguments, allowing the driver to perform the necessary MMIO writes for routing.

- Sink Manipulation:
- Standard RISC-V: Managed through dedicated drivers (init during probe, access via callbacks during dump).
- SiFive Implementation: Sink regs are embedded in Encoder/Funnel MMIO.
- We propose that the SiFive encoder and funnel drivers:
a. Initialize their supported sinks during the driver probe
sequence.
b. Implement and register the appropriate sink access callbacks,
ensuring they align with the standard perf log dumping workflow.

Quoted 2 lines

Do you think the proposed solution is feasible? I would appreciate your feedback.

Overall, I support the idea of accommodating SiFive trace under the common rvtrace infrastructure because we will be anyway doing the same thing for the upcoming self-hosted trace.

At this point, I can only try to ensure the rvtrace framework is generic enough. Dealing with SiFive specific fitment issues should be done incrementally as a separate exercise.

Regards,
Anup

Quoted 1,047 lines

Best regards,
Vincent

Quoted 1,042 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
 drivers/Makefile                             |   1 +
 drivers/hwtracing/Kconfig                    |   2 +
 drivers/hwtracing/rvtrace/Kconfig            |  16 +
 drivers/hwtracing/rvtrace/Makefile           |   4 +
 drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
 drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
 include/linux/rvtrace.h                      | 272 +++++++++++
 7 files changed, 953 insertions(+)
 create mode 100644 drivers/hwtracing/rvtrace/Kconfig
 create mode 100644 drivers/hwtracing/rvtrace/Makefile
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c
 create mode 100644 include/linux/rvtrace.h
drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)             += hwtracing/coresight/
 obj-y                          += hwtracing/intel_th/
 obj-$(CONFIG_STM)              += hwtracing/stm/
 obj-$(CONFIG_HISI_PTT)         += hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)          += hwtracing/rvtrace/
 obj-y                          += android/
 obj-$(CONFIG_NVMEM)            += nvmem/
 obj-$(CONFIG_FPGA)             += fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
 source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
 endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+       tristate "RISC-V Trace Support"
+       depends on RISCV
+       depends on OF
+       default RISCV
+       help
+         This framework provides a kernel interface for the RISC-V trace
+         drivers (including both e-trace and n-trace). It's intended to
+         build a topological view of the RISC-V trace components and
+         configure the right series of components when trace is enabled
+         on a CPU.
+
+         To compile this driver as a module, choose M here: the module
+         will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+       /* Lock to protect the type ID generator */
+       struct mutex lock;
+       struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+       int idx;
+
+       mutex_lock(&rvidx->lock);
+       idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+       mutex_unlock(&rvidx->lock);
+       if (idx < 0)
+               return idx;
+
+       comp->type_idx = idx;
+       return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+       mutex_lock(&rvidx->lock);
+       idr_remove(&rvidx->idr, comp->type_idx);
+       mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+       struct rvtrace_type_idx *rvidx;
+       int i;
+
+       for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+               rvidx = &rvtrace_type_idx_array[i];
+               mutex_init(&rvidx->lock);
+               idr_init(&rvidx->idr);
+       }
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids)
+{
+       const struct rvtrace_component_id *id;
+
+       for (id = ids; id->version && id->type; id++) {
+               if (comp->id.type == id->type &&
+                   comp->id.version == id->version)
+                       return id;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       int ret = -ENODEV;
+
+       if (!rtdrv->probe)
+               return ret;
+
+       ret = rtdrv->probe(comp);
+       if (!ret)
+               comp->ready = true;
+
+       return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       comp->ready = false;
+       if (rtdrv->remove)
+               rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+       .name   = "rvtrace",
+       .match  = rvtrace_match_device,
+       .probe  = rvtrace_probe,
+       .remove = rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+       struct fwnode_handle *fwnode;
+       struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_fwnode_match_data *d = data;
+
+       if (device_match_fwnode(&comp->dev, d->fwnode)) {
+               d->match = comp;
+               return 1;
+       }
+
+       return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+       struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+       int ret;
+
+       ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout)
+{
+       int i = 10;
+       u32 val;
+
+       while (i--) {
+               val = rvtrace_read32(pdata, offset);
+               if (((val >> bit) & 0x1) == bitval)
+                       break;
+               udelay(timeout);
+       }
+
+       return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+       if (!cpu_present(cpu))
+               return NULL;
+
+       return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i] != conn)
+                               continue;
+                       pdata->inconns[i] = NULL;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+       }
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i])
+                               continue;
+                       pdata->inconns[i] = conn;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i, ret;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+               if (ret < 0) {
+                       rvtrace_cleanup_inconns_from_outconns(comp);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       fwnode_handle_put(comp->dev.fwnode);
+       rvtrace_free_type_idx(comp);
+       kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+       int ret;
+
+       rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+       ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                              RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+                              pdata->control_poll_timeout_usecs);
+       if (ret)
+               return ret;
+
+       rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+                       RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+                               pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+       struct rvtrace_connection *conn;
+       struct rvtrace_component *comp;
+       u32 impl, type, major, minor;
+       int i, ret = 0;
+
+       if (!pdata || !pdata->dev) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       for (i = 0; i < pdata->nr_inconns; i++) {
+               if (pdata->inconns[i]) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               if (!conn || conn->src_port < 0 || conn->src_comp ||
+                   !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+                   conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       ret = rvtrace_component_reset(pdata);
+       if (ret)
+               goto err_out;
+
+       impl = rvtrace_read32(pdata, 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;
+
+       if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+       if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+       if (!comp) {
+               ret = -ENOMEM;
+               goto err_out;
+       }
+       comp->pdata = pdata;
+       comp->id.type = type;
+       comp->id.version = rvtrace_component_mkversion(major, minor);
+       ret = rvtrace_alloc_type_idx(comp);
+       if (ret) {
+               kfree(comp);
+               goto err_out;
+       }
+
+       comp->dev.parent = pdata->dev;
+       comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+       comp->dev.release = rvtrace_component_release;
+       comp->dev.bus = &rvtrace_bustype;
+       comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+       switch (comp->id.type) {
+       case RVTRACE_COMPONENT_TYPE_ENCODER:
+               dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_FUNNEL:
+               dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_RAMSINK:
+               dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_PIBSINK:
+               dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+               dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+               break;
+       default:
+               dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+               break;
+       }
+
+       mutex_lock(&rvtrace_mutex);
+
+       ret = device_register(&comp->dev);
+       if (ret) {
+               put_device(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               conn->src_comp = comp;
+       }
+
+       ret = rvtrace_setup_inconns_from_outconns(comp);
+       if (ret < 0) {
+               device_unregister(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               rvtrace_get_component(comp);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+       }
+
+       mutex_unlock(&rvtrace_mutex);
+
+       return comp;
+
+err_out_unlock:
+       mutex_unlock(&rvtrace_mutex);
+err_out:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+       struct rvtrace_component *c;
+
+       mutex_lock(&rvtrace_mutex);
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+               rvtrace_put_component(c);
+       }
+
+       rvtrace_cleanup_inconns_from_outconns(comp);
+       device_unregister(&comp->dev);
+
+       mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+       rtdrv->driver.owner = owner;
+       rtdrv->driver.bus = &rvtrace_bustype;
+
+       return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+       int ret;
+
+       rvtrace_init_type_idx();
+
+       ret = bus_register(&rvtrace_bustype);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&rvtrace_platform_driver);
+       if (ret) {
+               bus_unregister(&rvtrace_bustype);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+       platform_driver_unregister(&rvtrace_platform_driver);
+       bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +174 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+       struct rvtrace_connection *conn;
+       struct of_endpoint ep, rep;
+       int ret = 0, i = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+       pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+                                      sizeof(*pdata->outconns), GFP_KERNEL);
+       if (!pdata->outconns) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       for_each_endpoint_of_node(parent, ep_node) {
+               conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+               if (!conn) {
+                       of_node_put(ep_node);
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = of_graph_parse_endpoint(ep_node, &ep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               rep_node = of_graph_get_remote_endpoint(ep_node);
+               if (!rep_node) {
+                       ret = -ENODEV;
+                       of_node_put(ep_node);
+                       break;
+               }
+               rdev_node = of_graph_get_port_parent(rep_node);
+
+               ret = of_graph_parse_endpoint(rep_node, &rep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               conn->src_port = ep.port;
+               conn->src_fwnode = dev_fwnode(pdata->dev);
+               /* The 'src_comp' is set by rvtrace_register_component() */
+               conn->src_comp = NULL;
+               conn->dest_port = rep.port;
+               conn->dest_fwnode = of_fwnode_handle(rdev_node);
+               conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+               if (!conn->dest_comp) {
+                       ret = -EPROBE_DEFER;
+                       of_node_put(ep_node);
+               }
+
+               pdata->outconns[i] = conn;
+               i++;
+       }
+
+done:
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent;
+       int ret = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+       pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+                                     sizeof(*pdata->inconns), GFP_KERNEL);
+       if (!pdata->inconns)
+               ret = -ENOMEM;
+
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+       struct rvtrace_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct rvtrace_component *comp;
+       struct device_node *node;
+       struct resource *res;
+       int ret;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+       pdata->dev = dev;
+       pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       pdata->io_mem = true;
+       pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!pdata->base)
+               return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+       pdata->bound_cpu = -1;
+       node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+       if (node) {
+               ret = of_cpu_node_to_id(node);
+               of_node_put(node);
+               if (ret < 0)
+                       return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+               pdata->bound_cpu = ret;
+       }
+
+       /* Default control poll timeout */
+       pdata->control_poll_timeout_usecs = 10;
+
+       ret = rvtrace_of_parse_outconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+       ret = rvtrace_of_parse_inconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+       comp = rvtrace_register_component(pdata);
+       if (IS_ERR(comp))
+               return PTR_ERR(comp);
+
+       platform_set_drvdata(pdev, comp);
+       return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+       struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+       rvtrace_unregister_component(comp);
+}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+       { .compatible = "riscv,trace-component" },
+       {}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+       .driver = {
+               .name           = "rvtrace",
+               .of_match_table = rvtrace_platform_match,
+       },
+       .probe = rvtrace_platform_probe,
+       .remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +272 -1
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+       RVTRACE_COMPONENT_IMPID_UNKNOWN,
+       RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+       int src_port;
+       struct fwnode_handle *src_fwnode;
+       int dest_port;
+       struct fwnode_handle *dest_fwnode;
+       struct rvtrace_component *src_comp;
+       struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+       struct device *dev;
+
+       enum rvtrace_component_impid impid;
+
+       bool io_mem;
+       union {
+               void __iomem *base;
+               struct {
+                       u32 (*read)(struct rvtrace_platform_data *pdata,
+                                   u32 offset, bool relaxed);
+                       void (*write)(struct rvtrace_platform_data *pdata,
+                                     u32 val, u32 offset, bool relaxed);
+               };
+       };
+
+       int bound_cpu;
+
+       /* Delay in microseconds when polling control register bits */
+       int control_poll_timeout_usecs;
+
+       /*
+        * Platform driver must only populate empty pointer array without
+        * any actual input connections.
+        */
+       unsigned int nr_inconns;
+       struct rvtrace_connection **inconns;
+
+       /*
+        * Platform driver must fully populate pointer array with individual
+        * array elements pointing to actual output connections. The src_comp
+        * of each output connection is automatically updated at the time of
+        * registering component.
+        */
+       unsigned int nr_outconns;
+       struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl(pdata->base + offset);
+
+       return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl_relaxed(pdata->base + offset);
+
+       return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+                                          u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel_relaxed(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+       struct rvtrace_platform_data *pdata;
+       struct rvtrace_component_id id;
+       u32 type_idx;
+       struct device dev;
+       bool ready;
+};
+
+#define to_rvtrace_component(__dev)    container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+       get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+       put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+       const struct rvtrace_component_id *id_table;
+       int                     (*probe)(struct rvtrace_component *comp);
+       void                    (*remove)(struct rvtrace_component *comp);
+       struct device_driver    driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+       ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+       if (rtdrv)
+               driver_unregister(&rtdrv->driver);
+}
+
+#endif
--

2.43.0

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

#9 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

Hi Anup, Vincent,

I'm an active user of HiFive Premier P550. I've got full system stack trace collection and decoding working with:

https://github.com/ganboing/riscv-trace-umd
https://github.com/ganboing/libnexus-rv

Hence, I'm fully aware of the situation, and the spec differences between the pre-ratified spec (sifive) and the ratified one. I see the following path forward:

- Still prioritize the support for ratified spec, and add the funnel comp. Once rvtrace for 1.0 is in, then we can start working on supporting the pre-ratified ones. There aren't many people interested in reviewing it, except Nutty, Rob and me, and I'm the only one reviewing the riscv trace related code, so please start reviewing it if you can or get more folks. Latest is v2: https://lore.kernel.org/all/[email protected]/

- For pre-ratified trace systems such as the one in p550, I agree with most of the comments from you and Anup. I want to add:

Specifically for the "Sifive PMU" you are talking about, I don't think it's needed. I know p550 support ICT (In-Circuit Trace), which I assume is the "custom event" you were talking about. But still, they are encoded in N-trace format. I think this is closely related to the upcoming event trace spec, where you only enable a subset of messages during trace to capture events, rather than the whole control flow. Thus, we'll need a more generic way of dealing with it. I'd like to propose that we embed some crucial trace impl/runtime parameters in the metadata when producing perf.data E.g., the trace format (e-trace/n-trace) for sure, so that from a perf.data, it can be decoded anywhere without specifying other params. I know a typical N-trace may not need any parameters except the length of srcID (trTeSrcBits), but clearly E-trace would need more, and regarding event trace, we can, say, put the events enabled and some vendor specific info there. For trace collection (invoking perf), these vendor specific configuration can be passed in some vendor parameters/flags.

I'll closely follow this series, and I'll jump in for the p550 work if my time permits. This p550 (EIC7700) is the only silicon with N-trace that I can get my hands on, so in fact I'll need it to do a real E2E testing of rvtrace. BTW, beware that the p550 (EIC7700) trace does not strictly follow RISC-V-Trace-Control-Interface-Proposed-20200612.pdf. Hence, you actually need to refer to
https://github.com/eswincomputing/EIC7700X-SoC-Technical-Reference-Manual/releases/download/v1.0.0-20250103/EIC7700X_SoC_Technical_Reference_Manual_Part1.pdf
Chapter 3.4.1.19

Bo

Quoted 1,216 lines · Anup Patel
Quoted 28 lines · Vincent Chen

Hi Anup,

Thank you for providing the RVtrace infrastructure in Linux. It is great to see support for manipulating RISC-V trace components via the Linux perf tool.

SiFive provides a similar instruction trace specification that allows users to collect processor execution history and other events on SiFive platforms. This specification is implemented on the HiFive Premier P550 board. The specification is available here:
https://lists.riscv.org/g/tech-nexus/files/RISC-V-Trace-Control-Interface-Proposed-20200612.pdf

The fundamental concepts behind both specifications are aligned:
1. Both instruction trace systems are composed of Trace Encoders, Trace Funnels, and Trace Sinks.
a. Trace encoder: Each hart has its own trace encoder. It monitors
the ingress port to record addresses where program flow discontinuities occur. It compresses the log using either the Efficient Trace for RISC-V or RISC-V N-Trace specification (SiFive encoders support Nexus-based trace only) and transmits it to a downstream Trace Funnel or Sink.
b. Trace funnel: Functionally identical in both systems, the funnel
aggregates traces from multiple inputs (encoders or other funnels) and forwards the combined stream to a designated sink.
c. Trace sink: Both specifications support SRAM, System Memory, PIB,
and ATB Bridge sinks.

Quoted 5 lines

2. The control flow for starting and stopping recording is also consistent:
a. Start tracing: Proceed from the downstream sink up to the trace
encoder to ensure sinks are ready to capture logs.
b. Stop tracing: Proceed from the trace encoder down to the sink to
ensure all logs are flushed.

It's good that at high-level the SiFive trace is close to the ratified RISC-V trace so we won't have major re-work in the rvtrace framework.

Quoted 6 lines

However, these two specifications have some differences in implementation, primarily in:
1. MMIO Register Definitions: While functionally similar, the register layouts for SiFive encoders and funnels differ from the RISC-V standard.

The rvtrace framework (particularly rvtrace_register_component()) implicitly accesses few registers assuming components are RISC-V trace compliant which will break for SiFive trace components. I will try to make the rvtrace framework a little more generic in the next revision.

Quoted 3 lines

2. Lack of Independent Sink Components: In the SiFive implementation, sink configuration registers are embedded within the trace encoder and funnel MMIO regions, rather than existing as standalone components.

This is going to be the case for the self-hosted trace as well so this aspect needs to be addressed anyway in the future.

Quoted 19 lines

3. Trace Path configuration:
a. RISC-V trace component is allowed to pass down the trace log to a
single or multiple next-level trace components, depending on the HW capabilities and connection. This trace component cannot configure the output target via the MMIO register, but the next-level trace component has the ability to disable the inputs from a specific trace component.
b. The SiFive trace component can only output the trace log to a
designated sink, which is specified in the corresponding MMIO register. The designated sink cannot be configured to disable any input.
4. SiFive customer feature: SiFive platforms include custom event collection capabilities that require a dedicated PMU registration to expose via the Linux perf tool.

Given the similarities in control flow, we propose integrating the SiFive trace driver into your RVtrace infrastructure to avoid code duplication. Below is our proposed integration approach:

Quoted 5 lines

1. DT File (Device Tree)
- Inherit the current dt-bindings for RISC-V trace components, specifically the in-ports and out-ports descriptions for topology.
- Add "sifive,trace-component" to the compatible string of SiFive nodes.

New compatible string is mandatory because the SiFive trace component is not directly compatible with the RISC-V trace component.

Quoted 4 lines

- Add a new DT property to indicate the specific type of SiFive trace component. This is necessary because the corresponding register in the SiFive specification does not expose the component's type (Encoder/Funnel) to software.

Rather than new DT property, I would suggest using separate compatible strings (such as sifive,trace-encoder and sifive,trace-funnel or something similar).

Quoted 9 lines

2. Perf Tool
- Register a new perf PMU specifically for SiFive trace. This also allows us to define custom event configurations for SiFive-specific features.
- The Perf tool will support both PMUs simultaneously. The final selected operating PMU depends on the PMU name in the perf command and the platform capability.
- Leverage the callback functions registered to "struct auxtrace_record" as much as possible.

IMO, a separate perf PMU instance for SiFive should be the last option. This certainly needs more brainstorming in the RISE kernel working group.

Quoted 42 lines

3. Linux Device Driver
- Device Probe:
- Add "sifive,trace-component" to rvtrace_platform_match[] to enable rvtrace_platform_probe() to support SiFive trace components.
- Update rvtrace_register_component() to support registering SiFive encoders and funnels to the rvtrace bus.
- Plan to let the SiFive trace encoder and funnel share the same type ID as RISC-V. In this condition, to enable the driver to distinguish between SiFive and RISC-V hardware, it would be necessary to add a new vendor ID field into
"struct rvtrace_component_id".
- Driver Implementations:
- Like other drivers of RISC-V trace components, the SiFive encoder and funnel driver will implement the required functions in the rvtrace_driver.
- Trace Delivery Path (Encoder to Sink):
- This infrastructure can be used in the same way to establish the topology of the entire trace system via the DT file.
- Based on the same topology descriptions, the trace delivery path can use the same logic to create.
- The primary difference lies in the trace path configuration. To address this, we propose delegating the path configuration logic to the .start() and .stop() callbacks in "struct rvtrace_driver". This allows the routing implementation to be encapsulated within the specific driver. To support this, we would need to update the definition of the .start() and .stop() callbacks to accept the adjacent node information as arguments, allowing the driver to perform the necessary MMIO writes for routing.

- Sink Manipulation:
- Standard RISC-V: Managed through dedicated drivers (init during probe, access via callbacks during dump).
- SiFive Implementation: Sink regs are embedded in Encoder/Funnel MMIO.
- We propose that the SiFive encoder and funnel drivers:
a. Initialize their supported sinks during the driver probe
sequence.
b. Implement and register the appropriate sink access callbacks,
ensuring they align with the standard perf log dumping workflow.

Quoted 2 lines

Do you think the proposed solution is feasible? I would appreciate your feedback.

Overall, I support the idea of accommodating SiFive trace under the common rvtrace infrastructure because we will be anyway doing the same thing for the upcoming self-hosted trace.

At this point, I can only try to ensure the rvtrace framework is generic enough. Dealing with SiFive specific fitment issues should be done incrementally as a separate exercise.

Regards,
Anup

Quoted 1,043 lines

Best regards,
Vincent

Quoted 1,038 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
  drivers/Makefile                             |   1 +
  drivers/hwtracing/Kconfig                    |   2 +
  drivers/hwtracing/rvtrace/Kconfig            |  16 +
  drivers/hwtracing/rvtrace/Makefile           |   4 +
  drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
  drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
  include/linux/rvtrace.h                      | 272 +++++++++++
  7 files changed, 953 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/Kconfig create mode 100644 drivers/hwtracing/rvtrace/Makefile create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c create mode 100644 include/linux/rvtrace.h

drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)             += hwtracing/coresight/
  obj-y                          += hwtracing/intel_th/
  obj-$(CONFIG_STM)              += hwtracing/stm/
  obj-$(CONFIG_HISI_PTT)         += hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)          += hwtracing/rvtrace/
  obj-y                          += android/
  obj-$(CONFIG_NVMEM)            += nvmem/
  obj-$(CONFIG_FPGA)             += fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
  source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
  endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+       tristate "RISC-V Trace Support"
+       depends on RISCV
+       depends on OF
+       default RISCV
+       help
+         This framework provides a kernel interface for the RISC-V trace
+         drivers (including both e-trace and n-trace). It's intended to
+         build a topological view of the RISC-V trace components and
+         configure the right series of components when trace is enabled
+         on a CPU.
+
+         To compile this driver as a module, choose M here: the module
+         will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+       /* Lock to protect the type ID generator */
+       struct mutex lock;
+       struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+       int idx;
+
+       mutex_lock(&rvidx->lock);
+       idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+       mutex_unlock(&rvidx->lock);
+       if (idx < 0)
+               return idx;
+
+       comp->type_idx = idx;
+       return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+       mutex_lock(&rvidx->lock);
+       idr_remove(&rvidx->idr, comp->type_idx);
+       mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+       struct rvtrace_type_idx *rvidx;
+       int i;
+
+       for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+               rvidx = &rvtrace_type_idx_array[i];
+               mutex_init(&rvidx->lock);
+               idr_init(&rvidx->idr);
+       }
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids)
+{
+       const struct rvtrace_component_id *id;
+
+       for (id = ids; id->version && id->type; id++) {
+               if (comp->id.type == id->type &&
+                   comp->id.version == id->version)
+                       return id;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       int ret = -ENODEV;
+
+       if (!rtdrv->probe)
+               return ret;
+
+       ret = rtdrv->probe(comp);
+       if (!ret)
+               comp->ready = true;
+
+       return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       comp->ready = false;
+       if (rtdrv->remove)
+               rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+       .name   = "rvtrace",
+       .match  = rvtrace_match_device,
+       .probe  = rvtrace_probe,
+       .remove = rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+       struct fwnode_handle *fwnode;
+       struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_fwnode_match_data *d = data;
+
+       if (device_match_fwnode(&comp->dev, d->fwnode)) {
+               d->match = comp;
+               return 1;
+       }
+
+       return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+       struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+       int ret;
+
+       ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout)
+{
+       int i = 10;
+       u32 val;
+
+       while (i--) {
+               val = rvtrace_read32(pdata, offset);
+               if (((val >> bit) & 0x1) == bitval)
+                       break;
+               udelay(timeout);
+       }
+
+       return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+       if (!cpu_present(cpu))
+               return NULL;
+
+       return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i] != conn)
+                               continue;
+                       pdata->inconns[i] = NULL;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+       }
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i])
+                               continue;
+                       pdata->inconns[i] = conn;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i, ret;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+               if (ret < 0) {
+                       rvtrace_cleanup_inconns_from_outconns(comp);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       fwnode_handle_put(comp->dev.fwnode);
+       rvtrace_free_type_idx(comp);
+       kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+       int ret;
+
+       rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+       ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                              RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+                              pdata->control_poll_timeout_usecs);
+       if (ret)
+               return ret;
+
+       rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+                       RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+                               pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+       struct rvtrace_connection *conn;
+       struct rvtrace_component *comp;
+       u32 impl, type, major, minor;
+       int i, ret = 0;
+
+       if (!pdata || !pdata->dev) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       for (i = 0; i < pdata->nr_inconns; i++) {
+               if (pdata->inconns[i]) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               if (!conn || conn->src_port < 0 || conn->src_comp ||
+                   !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+                   conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       ret = rvtrace_component_reset(pdata);
+       if (ret)
+               goto err_out;
+
+       impl = rvtrace_read32(pdata, 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;
+
+       if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+       if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+       if (!comp) {
+               ret = -ENOMEM;
+               goto err_out;
+       }
+       comp->pdata = pdata;
+       comp->id.type = type;
+       comp->id.version = rvtrace_component_mkversion(major, minor);
+       ret = rvtrace_alloc_type_idx(comp);
+       if (ret) {
+               kfree(comp);
+               goto err_out;
+       }
+
+       comp->dev.parent = pdata->dev;
+       comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+       comp->dev.release = rvtrace_component_release;
+       comp->dev.bus = &rvtrace_bustype;
+       comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+       switch (comp->id.type) {
+       case RVTRACE_COMPONENT_TYPE_ENCODER:
+               dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_FUNNEL:
+               dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_RAMSINK:
+               dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_PIBSINK:
+               dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+               dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+               break;
+       default:
+               dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+               break;
+       }
+
+       mutex_lock(&rvtrace_mutex);
+
+       ret = device_register(&comp->dev);
+       if (ret) {
+               put_device(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               conn->src_comp = comp;
+       }
+
+       ret = rvtrace_setup_inconns_from_outconns(comp);
+       if (ret < 0) {
+               device_unregister(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               rvtrace_get_component(comp);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+       }
+
+       mutex_unlock(&rvtrace_mutex);
+
+       return comp;
+
+err_out_unlock:
+       mutex_unlock(&rvtrace_mutex);
+err_out:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+       struct rvtrace_component *c;
+
+       mutex_lock(&rvtrace_mutex);
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+               rvtrace_put_component(c);
+       }
+
+       rvtrace_cleanup_inconns_from_outconns(comp);
+       device_unregister(&comp->dev);
+
+       mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+       rtdrv->driver.owner = owner;
+       rtdrv->driver.bus = &rvtrace_bustype;
+
+       return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+       int ret;
+
+       rvtrace_init_type_idx();
+
+       ret = bus_register(&rvtrace_bustype);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&rvtrace_platform_driver);
+       if (ret) {
+               bus_unregister(&rvtrace_bustype);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+       platform_driver_unregister(&rvtrace_platform_driver);
+       bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +174 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+       struct rvtrace_connection *conn;
+       struct of_endpoint ep, rep;
+       int ret = 0, i = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+       pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+                                      sizeof(*pdata->outconns), GFP_KERNEL);
+       if (!pdata->outconns) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       for_each_endpoint_of_node(parent, ep_node) {
+               conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+               if (!conn) {
+                       of_node_put(ep_node);
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = of_graph_parse_endpoint(ep_node, &ep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               rep_node = of_graph_get_remote_endpoint(ep_node);
+               if (!rep_node) {
+                       ret = -ENODEV;
+                       of_node_put(ep_node);
+                       break;
+               }
+               rdev_node = of_graph_get_port_parent(rep_node);
+
+               ret = of_graph_parse_endpoint(rep_node, &rep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               conn->src_port = ep.port;
+               conn->src_fwnode = dev_fwnode(pdata->dev);
+               /* The 'src_comp' is set by rvtrace_register_component() */
+               conn->src_comp = NULL;
+               conn->dest_port = rep.port;
+               conn->dest_fwnode = of_fwnode_handle(rdev_node);
+               conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+               if (!conn->dest_comp) {
+                       ret = -EPROBE_DEFER;
+                       of_node_put(ep_node);
+               }
+
+               pdata->outconns[i] = conn;
+               i++;
+       }
+
+done:
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent;
+       int ret = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+       pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+                                     sizeof(*pdata->inconns), GFP_KERNEL);
+       if (!pdata->inconns)
+               ret = -ENOMEM;
+
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+       struct rvtrace_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct rvtrace_component *comp;
+       struct device_node *node;
+       struct resource *res;
+       int ret;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+       pdata->dev = dev;
+       pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       pdata->io_mem = true;
+       pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!pdata->base)
+               return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+       pdata->bound_cpu = -1;
+       node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+       if (node) {
+               ret = of_cpu_node_to_id(node);
+               of_node_put(node);
+               if (ret < 0)
+                       return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+               pdata->bound_cpu = ret;
+       }
+
+       /* Default control poll timeout */
+       pdata->control_poll_timeout_usecs = 10;
+
+       ret = rvtrace_of_parse_outconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+       ret = rvtrace_of_parse_inconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+       comp = rvtrace_register_component(pdata);
+       if (IS_ERR(comp))
+               return PTR_ERR(comp);
+
+       platform_set_drvdata(pdev, comp);
+       return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+       struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+       rvtrace_unregister_component(comp);
+}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+       { .compatible = "riscv,trace-component" },
+       {}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+       .driver = {
+               .name           = "rvtrace",
+               .of_match_table = rvtrace_platform_match,
+       },
+       .probe = rvtrace_platform_probe,
+       .remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +272 -1
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+       RVTRACE_COMPONENT_IMPID_UNKNOWN,
+       RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+       int src_port;
+       struct fwnode_handle *src_fwnode;
+       int dest_port;
+       struct fwnode_handle *dest_fwnode;
+       struct rvtrace_component *src_comp;
+       struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+       struct device *dev;
+
+       enum rvtrace_component_impid impid;
+
+       bool io_mem;
+       union {
+               void __iomem *base;
+               struct {
+                       u32 (*read)(struct rvtrace_platform_data *pdata,
+                                   u32 offset, bool relaxed);
+                       void (*write)(struct rvtrace_platform_data *pdata,
+                                     u32 val, u32 offset, bool relaxed);
+               };
+       };
+
+       int bound_cpu;
+
+       /* Delay in microseconds when polling control register bits */
+       int control_poll_timeout_usecs;
+
+       /*
+        * Platform driver must only populate empty pointer array without
+        * any actual input connections.
+        */
+       unsigned int nr_inconns;
+       struct rvtrace_connection **inconns;
+
+       /*
+        * Platform driver must fully populate pointer array with individual
+        * array elements pointing to actual output connections. The src_comp
+        * of each output connection is automatically updated at the time of
+        * registering component.
+        */
+       unsigned int nr_outconns;
+       struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl(pdata->base + offset);
+
+       return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl_relaxed(pdata->base + offset);
+
+       return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+                                          u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel_relaxed(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+       struct rvtrace_platform_data *pdata;
+       struct rvtrace_component_id id;
+       u32 type_idx;
+       struct device dev;
+       bool ready;
+};
+
+#define to_rvtrace_component(__dev)    container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+       get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+       put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+       const struct rvtrace_component_id *id_table;
+       int                     (*probe)(struct rvtrace_component *comp);
+       void                    (*remove)(struct rvtrace_component *comp);
+       struct device_driver    driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+       ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+       if (rtdrv)
+               driver_unregister(&rtdrv->driver);
+}
+
+#endif
--

2.43.0

Quoted 4 lines
Quoted 4 lines

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

#10 lore raw

Re: [PATCH 02/11] rvtrace: Initial implementation of driver framework

Hi Anup and Bo Gan,

First, I apologize for the delayed response. We really appreciate your openness to including the SiFive (pre-ratified) version in the current RVtrace infrastructure. My colleagues and I took some time to look into your feedback, and we agree that the SiFive implementation should be able to share the same perf PMU interface as RVtrace. My colleague, Eric Lin, is currently preparing an RFC patch series based on Anup's latest v3 patch set to demonstrate how we can integrate the pre-ratified version. We plan to send it out in the coming weeks and look forward to using it as a basis to discuss the details further.

Thanks, Vincent Chen

Quoted 1,268 lines · Bo Gan

Hi Anup, Vincent,

I'm an active user of HiFive Premier P550. I've got full system stack trace collection and decoding working with:

https://github.com/ganboing/riscv-trace-umd
https://github.com/ganboing/libnexus-rv

Hence, I'm fully aware of the situation, and the spec differences between the pre-ratified spec (sifive) and the ratified one. I see the following path forward:

- Still prioritize the support for ratified spec, and add the funnel comp. Once rvtrace for 1.0 is in, then we can start working on supporting the pre-ratified ones. There aren't many people interested in reviewing it, except Nutty, Rob and me, and I'm the only one reviewing the riscv trace related code, so please start reviewing it if you can or get more folks. Latest is v2: https://lore.kernel.org/all/[email protected]/

- For pre-ratified trace systems such as the one in p550, I agree with most of the comments from you and Anup. I want to add:

Specifically for the "Sifive PMU" you are talking about, I don't think it's needed. I know p550 support ICT (In-Circuit Trace), which I assume is the "custom event" you were talking about. But still, they are encoded in N-trace format. I think this is closely related to the upcoming event trace spec, where you only enable a subset of messages during trace to capture events, rather than the whole control flow. Thus, we'll need a more generic way of dealing with it. I'd like to propose that we embed some crucial trace impl/runtime parameters in the metadata when producing perf.data E.g., the trace format (e-trace/n-trace) for sure, so that from a perf.data, it can be decoded anywhere without specifying other params. I know a typical N-trace may not need any parameters except the length of srcID (trTeSrcBits), but clearly E-trace would need more, and regarding event trace, we can, say, put the events enabled and some vendor specific info there. For trace collection (invoking perf), these vendor specific configuration can be passed in some vendor parameters/flags.

I'll closely follow this series, and I'll jump in for the p550 work if my time permits. This p550 (EIC7700) is the only silicon with N-trace that I can get my hands on, so in fact I'll need it to do a real E2E testing of rvtrace. BTW, beware that the p550 (EIC7700) trace does not strictly follow RISC-V-Trace-Control-Interface-Proposed-20200612.pdf. Hence, you actually need to refer to
https://github.com/eswincomputing/EIC7700X-SoC-Technical-Reference-Manual/releases/download/v1.0.0-20250103/EIC7700X_SoC_Technical_Reference_Manual_Part1.pdf
Chapter 3.4.1.19

Bo

Quoted 1,216 lines · Anup Patel
Quoted 28 lines · Vincent Chen

Hi Anup,

Thank you for providing the RVtrace infrastructure in Linux. It is great to see support for manipulating RISC-V trace components via the Linux perf tool.

SiFive provides a similar instruction trace specification that allows users to collect processor execution history and other events on SiFive platforms. This specification is implemented on the HiFive Premier P550 board. The specification is available here:
https://lists.riscv.org/g/tech-nexus/files/RISC-V-Trace-Control-Interface-Proposed-20200612.pdf

The fundamental concepts behind both specifications are aligned:
1. Both instruction trace systems are composed of Trace Encoders, Trace Funnels, and Trace Sinks.
a. Trace encoder: Each hart has its own trace encoder. It monitors
the ingress port to record addresses where program flow discontinuities occur. It compresses the log using either the Efficient Trace for RISC-V or RISC-V N-Trace specification (SiFive encoders support Nexus-based trace only) and transmits it to a downstream Trace Funnel or Sink.
b. Trace funnel: Functionally identical in both systems, the funnel
aggregates traces from multiple inputs (encoders or other funnels) and forwards the combined stream to a designated sink.
c. Trace sink: Both specifications support SRAM, System Memory, PIB,
and ATB Bridge sinks.

Quoted 5 lines

2. The control flow for starting and stopping recording is also consistent:
a. Start tracing: Proceed from the downstream sink up to the trace
encoder to ensure sinks are ready to capture logs.
b. Stop tracing: Proceed from the trace encoder down to the sink to
ensure all logs are flushed.

It's good that at high-level the SiFive trace is close to the ratified RISC-V trace so we won't have major re-work in the rvtrace framework.

Quoted 6 lines

However, these two specifications have some differences in implementation, primarily in:
1. MMIO Register Definitions: While functionally similar, the register layouts for SiFive encoders and funnels differ from the RISC-V standard.

The rvtrace framework (particularly rvtrace_register_component()) implicitly accesses few registers assuming components are RISC-V trace compliant which will break for SiFive trace components. I will try to make the rvtrace framework a little more generic in the next revision.

Quoted 3 lines

2. Lack of Independent Sink Components: In the SiFive implementation, sink configuration registers are embedded within the trace encoder and funnel MMIO regions, rather than existing as standalone components.

This is going to be the case for the self-hosted trace as well so this aspect needs to be addressed anyway in the future.

Quoted 19 lines

3. Trace Path configuration:
a. RISC-V trace component is allowed to pass down the trace log to a
single or multiple next-level trace components, depending on the HW capabilities and connection. This trace component cannot configure the output target via the MMIO register, but the next-level trace component has the ability to disable the inputs from a specific trace component.
b. The SiFive trace component can only output the trace log to a
designated sink, which is specified in the corresponding MMIO register. The designated sink cannot be configured to disable any input.
4. SiFive customer feature: SiFive platforms include custom event collection capabilities that require a dedicated PMU registration to expose via the Linux perf tool.

Given the similarities in control flow, we propose integrating the SiFive trace driver into your RVtrace infrastructure to avoid code duplication. Below is our proposed integration approach:

Quoted 5 lines

1. DT File (Device Tree)
- Inherit the current dt-bindings for RISC-V trace components, specifically the in-ports and out-ports descriptions for topology.
- Add "sifive,trace-component" to the compatible string of SiFive nodes.

New compatible string is mandatory because the SiFive trace component is not directly compatible with the RISC-V trace component.

Quoted 4 lines

- Add a new DT property to indicate the specific type of SiFive trace component. This is necessary because the corresponding register in the SiFive specification does not expose the component's type (Encoder/Funnel) to software.

Rather than new DT property, I would suggest using separate compatible strings (such as sifive,trace-encoder and sifive,trace-funnel or something similar).

Quoted 9 lines

2. Perf Tool
- Register a new perf PMU specifically for SiFive trace. This also allows us to define custom event configurations for SiFive-specific features.
- The Perf tool will support both PMUs simultaneously. The final selected operating PMU depends on the PMU name in the perf command and the platform capability.
- Leverage the callback functions registered to "struct auxtrace_record" as much as possible.

IMO, a separate perf PMU instance for SiFive should be the last option. This certainly needs more brainstorming in the RISE kernel working group.

Quoted 42 lines

3. Linux Device Driver
- Device Probe:
- Add "sifive,trace-component" to rvtrace_platform_match[] to enable rvtrace_platform_probe() to support SiFive trace components.
- Update rvtrace_register_component() to support registering SiFive encoders and funnels to the rvtrace bus.
- Plan to let the SiFive trace encoder and funnel share the same type ID as RISC-V. In this condition, to enable the driver to distinguish between SiFive and RISC-V hardware, it would be necessary to add a new vendor ID field into
"struct rvtrace_component_id".
- Driver Implementations:
- Like other drivers of RISC-V trace components, the SiFive encoder and funnel driver will implement the required functions in the rvtrace_driver.
- Trace Delivery Path (Encoder to Sink):
- This infrastructure can be used in the same way to establish the topology of the entire trace system via the DT file.
- Based on the same topology descriptions, the trace delivery path can use the same logic to create.
- The primary difference lies in the trace path configuration. To address this, we propose delegating the path configuration logic to the .start() and .stop() callbacks in "struct rvtrace_driver". This allows the routing implementation to be encapsulated within the specific driver. To support this, we would need to update the definition of the .start() and .stop() callbacks to accept the adjacent node information as arguments, allowing the driver to perform the necessary MMIO writes for routing.

- Sink Manipulation:
- Standard RISC-V: Managed through dedicated drivers (init during probe, access via callbacks during dump).
- SiFive Implementation: Sink regs are embedded in Encoder/Funnel MMIO.
- We propose that the SiFive encoder and funnel drivers:
a. Initialize their supported sinks during the driver probe
sequence.
b. Implement and register the appropriate sink access callbacks,
ensuring they align with the standard perf log dumping workflow.

Quoted 2 lines

Do you think the proposed solution is feasible? I would appreciate your feedback.

Overall, I support the idea of accommodating SiFive trace under the common rvtrace infrastructure because we will be anyway doing the same thing for the upcoming self-hosted trace.

At this point, I can only try to ensure the rvtrace framework is generic enough. Dealing with SiFive specific fitment issues should be done incrementally as a separate exercise.

Regards,
Anup

Quoted 1,043 lines

Best regards,
Vincent

Quoted 1,038 lines · Anup Patel

The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component.

Implement a basic driver framework for 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.

[1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf

---
  drivers/Makefile                             |   1 +
  drivers/hwtracing/Kconfig                    |   2 +
  drivers/hwtracing/rvtrace/Kconfig            |  16 +
  drivers/hwtracing/rvtrace/Makefile           |   4 +
  drivers/hwtracing/rvtrace/rvtrace-core.c     | 484 +++++++++++++++++++
  drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++
  include/linux/rvtrace.h                      | 272 +++++++++++
  7 files changed, 953 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/Kconfig create mode 100644 drivers/hwtracing/rvtrace/Makefile create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c create mode 100644 include/linux/rvtrace.h

drivers/Makefile +1 -0
index b5749cf67044..466a55580f60 100644
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT)             += hwtracing/coresight/
  obj-y                          += hwtracing/intel_th/
  obj-$(CONFIG_STM)              += hwtracing/stm/
  obj-$(CONFIG_HISI_PTT)         += hwtracing/ptt/
+obj-$(CONFIG_RVTRACE)          += hwtracing/rvtrace/
  obj-y                          += android/
  obj-$(CONFIG_NVMEM)            += nvmem/
  obj-$(CONFIG_FPGA)             += fpga/
drivers/hwtracing/Kconfig +2 -0
index 911ee977103c..daeb38fe332d 100644
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
  source "drivers/hwtracing/ptt/Kconfig"
 
+source "drivers/hwtracing/rvtrace/Kconfig"
+
  endmenu
drivers/hwtracing/rvtrace/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f8f6feea1953
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig RVTRACE
+       tristate "RISC-V Trace Support"
+       depends on RISCV
+       depends on OF
+       default RISCV
+       help
+         This framework provides a kernel interface for the RISC-V trace
+         drivers (including both e-trace and n-trace). It's intended to
+         build a topological view of the RISC-V trace components and
+         configure the right series of components when trace is enabled
+         on a CPU.
+
+         To compile this driver as a module, choose M here: the module
+         will be called rvtrace.
drivers/hwtracing/rvtrace/Makefile new file +4 -0
new file mode 100644
index 000000000000..988525a379cf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_RVTRACE) += rvtrace.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o
drivers/hwtracing/rvtrace/rvtrace-core.c new file +484 -0
new file mode 100644
index 000000000000..52ea931745fc
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/rvtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(rvtrace_mutex);
+
+/* Per-CPU encoder instances */
+static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder);
+
+/* Component type based id generator */
+struct rvtrace_type_idx {
+       /* Lock to protect the type ID generator */
+       struct mutex lock;
+       struct idr idr;
+};
+
+/* Array of component type based id generator */
+static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TYPE_MAX];
+
+static int rvtrace_alloc_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+       int idx;
+
+       mutex_lock(&rvidx->lock);
+       idx = idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL);
+       mutex_unlock(&rvidx->lock);
+       if (idx < 0)
+               return idx;
+
+       comp->type_idx = idx;
+       return 0;
+}
+
+static void rvtrace_free_type_idx(struct rvtrace_component *comp)
+{
+       struct rvtrace_type_idx *rvidx = &rvtrace_type_idx_array[comp->id.type];
+
+       mutex_lock(&rvidx->lock);
+       idr_remove(&rvidx->idr, comp->type_idx);
+       mutex_unlock(&rvidx->lock);
+}
+
+static void __init rvtrace_init_type_idx(void)
+{
+       struct rvtrace_type_idx *rvidx;
+       int i;
+
+       for (i = 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) {
+               rvidx = &rvtrace_type_idx_array[i];
+               mutex_init(&rvidx->lock);
+               idr_init(&rvidx->idr);
+       }
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids)
+{
+       const struct rvtrace_component_id *id;
+
+       for (id = ids; id->version && id->type; id++) {
+               if (comp->id.type == id->type &&
+                   comp->id.version == id->version)
+                       return id;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rvtrace_match_id);
+
+static int rvtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(drv);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0;
+}
+
+static int rvtrace_probe(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       int ret = -ENODEV;
+
+       if (!rtdrv->probe)
+               return ret;
+
+       ret = rtdrv->probe(comp);
+       if (!ret)
+               comp->ready = true;
+
+       return ret;
+}
+
+static void rvtrace_remove(struct device *dev)
+{
+       const struct rvtrace_driver *rtdrv = to_rvtrace_driver(dev->driver);
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       comp->ready = false;
+       if (rtdrv->remove)
+               rtdrv->remove(comp);
+}
+
+const struct bus_type rvtrace_bustype = {
+       .name   = "rvtrace",
+       .match  = rvtrace_match_device,
+       .probe  = rvtrace_probe,
+       .remove = rvtrace_remove,
+};
+
+struct rvtrace_fwnode_match_data {
+       struct fwnode_handle *fwnode;
+       struct rvtrace_component *match;
+};
+
+static int rvtrace_match_fwnode(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_fwnode_match_data *d = data;
+
+       if (device_match_fwnode(&comp->dev, d->fwnode)) {
+               d->match = comp;
+               return 1;
+       }
+
+       return 0;
+}
+
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+       struct rvtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+       int ret;
+
+       ret = bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return d.match;
+}
+EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout)
+{
+       int i = 10;
+       u32 val;
+
+       while (i--) {
+               val = rvtrace_read32(pdata, offset);
+               if (((val >> bit) & 0x1) == bitval)
+                       break;
+               udelay(timeout);
+       }
+
+       return (i < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_poll_bit);
+
+int rvtrace_enable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_enable_component);
+
+int rvtrace_disable_component(struct rvtrace_component *comp)
+{
+       u32 val;
+
+       val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+       val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT);
+       rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0,
+                               comp->pdata->control_poll_timeout_usecs);
+}
+EXPORT_SYMBOL_GPL(rvtrace_disable_component);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
+{
+       if (!cpu_present(cpu))
+               return NULL;
+
+       return per_cpu(rvtrace_cpu_encoder, cpu);
+}
+EXPORT_SYMBOL_GPL(rvtrace_cpu_source);
+
+static int rvtrace_cleanup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i] != conn)
+                               continue;
+                       pdata->inconns[i] = NULL;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn);
+       }
+}
+
+static int rvtrace_setup_inconn(struct device *dev, void *data)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn = data;
+       int i;
+
+       if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+               for (i = 0; i < pdata->nr_inconns; i++) {
+                       if (pdata->inconns[i])
+                               continue;
+                       pdata->inconns[i] = conn;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *comp)
+{
+       struct rvtrace_platform_data *pdata = comp->pdata;
+       struct rvtrace_connection *conn;
+       int i, ret;
+
+       lockdep_assert_held(&rvtrace_mutex);
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               ret = bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inconn);
+               if (ret < 0) {
+                       rvtrace_cleanup_inconns_from_outconns(comp);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static void rvtrace_component_release(struct device *dev)
+{
+       struct rvtrace_component *comp = to_rvtrace_component(dev);
+
+       fwnode_handle_put(comp->dev.fwnode);
+       rvtrace_free_type_idx(comp);
+       kfree(comp);
+}
+
+static int rvtrace_component_reset(struct rvtrace_platform_data *pdata)
+{
+       int ret;
+
+       rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET);
+       ret = rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                              RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0,
+                              pdata->control_poll_timeout_usecs);
+       if (ret)
+               return ret;
+
+       rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK,
+                       RVTRACE_COMPONENT_CTRL_OFFSET);
+       return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                               RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1,
+                               pdata->control_poll_timeout_usecs);
+}
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata)
+{
+       struct rvtrace_connection *conn;
+       struct rvtrace_component *comp;
+       u32 impl, type, major, minor;
+       int i, ret = 0;
+
+       if (!pdata || !pdata->dev) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       for (i = 0; i < pdata->nr_inconns; i++) {
+               if (pdata->inconns[i]) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               if (!conn || conn->src_port < 0 || conn->src_comp ||
+                   !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+                   conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       ret = rvtrace_component_reset(pdata);
+       if (ret)
+               goto err_out;
+
+       impl = rvtrace_read32(pdata, 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;
+
+       if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+       if (type == RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+       if (!comp) {
+               ret = -ENOMEM;
+               goto err_out;
+       }
+       comp->pdata = pdata;
+       comp->id.type = type;
+       comp->id.version = rvtrace_component_mkversion(major, minor);
+       ret = rvtrace_alloc_type_idx(comp);
+       if (ret) {
+               kfree(comp);
+               goto err_out;
+       }
+
+       comp->dev.parent = pdata->dev;
+       comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+       comp->dev.release = rvtrace_component_release;
+       comp->dev.bus = &rvtrace_bustype;
+       comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+       switch (comp->id.type) {
+       case RVTRACE_COMPONENT_TYPE_ENCODER:
+               dev_set_name(&comp->dev, "encoder-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_FUNNEL:
+               dev_set_name(&comp->dev, "funnel-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_RAMSINK:
+               dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_PIBSINK:
+               dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx);
+               break;
+       case RVTRACE_COMPONENT_TYPE_ATBBRIDGE:
+               dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx);
+               break;
+       default:
+               dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx);
+               break;
+       }
+
+       mutex_lock(&rvtrace_mutex);
+
+       ret = device_register(&comp->dev);
+       if (ret) {
+               put_device(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       for (i = 0; i < pdata->nr_outconns; i++) {
+               conn = pdata->outconns[i];
+               conn->src_comp = comp;
+       }
+
+       ret = rvtrace_setup_inconns_from_outconns(comp);
+       if (ret < 0) {
+               device_unregister(&comp->dev);
+               goto err_out_unlock;
+       }
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               rvtrace_get_component(comp);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = comp;
+       }
+
+       mutex_unlock(&rvtrace_mutex);
+
+       return comp;
+
+err_out_unlock:
+       mutex_unlock(&rvtrace_mutex);
+err_out:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_register_component);
+
+void rvtrace_unregister_component(struct rvtrace_component *comp)
+{
+       struct rvtrace_component *c;
+
+       mutex_lock(&rvtrace_mutex);
+
+       if (comp->id.type == RVTRACE_COMPONENT_TYPE_ENCODER) {
+               c = per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu);
+               per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) = NULL;
+               rvtrace_put_component(c);
+       }
+
+       rvtrace_cleanup_inconns_from_outconns(comp);
+       device_unregister(&comp->dev);
+
+       mutex_unlock(&rvtrace_mutex);
+}
+EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
+{
+       rtdrv->driver.owner = owner;
+       rtdrv->driver.bus = &rvtrace_bustype;
+
+       return driver_register(&rtdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__rvtrace_register_driver);
+
+static int __init rvtrace_init(void)
+{
+       int ret;
+
+       rvtrace_init_type_idx();
+
+       ret = bus_register(&rvtrace_bustype);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&rvtrace_platform_driver);
+       if (ret) {
+               bus_unregister(&rvtrace_bustype);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit rvtrace_exit(void)
+{
+       platform_driver_unregister(&rvtrace_platform_driver);
+       bus_unregister(&rvtrace_bustype);
+}
+
+module_init(rvtrace_init);
+module_exit(rvtrace_exit);
drivers/hwtracing/rvtrace/rvtrace-platform.c new file +174 -0
new file mode 100644
index 000000000000..a110ff1f2f08
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent, *ep_node, *rep_node, *rdev_node;
+       struct rvtrace_connection *conn;
+       struct of_endpoint ep, rep;
+       int ret = 0, i = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_outconns = of_graph_get_endpoint_count(parent);
+       pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns,
+                                      sizeof(*pdata->outconns), GFP_KERNEL);
+       if (!pdata->outconns) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       for_each_endpoint_of_node(parent, ep_node) {
+               conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL);
+               if (!conn) {
+                       of_node_put(ep_node);
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = of_graph_parse_endpoint(ep_node, &ep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               rep_node = of_graph_get_remote_endpoint(ep_node);
+               if (!rep_node) {
+                       ret = -ENODEV;
+                       of_node_put(ep_node);
+                       break;
+               }
+               rdev_node = of_graph_get_port_parent(rep_node);
+
+               ret = of_graph_parse_endpoint(rep_node, &rep);
+               if (ret) {
+                       of_node_put(ep_node);
+                       break;
+               }
+
+               conn->src_port = ep.port;
+               conn->src_fwnode = dev_fwnode(pdata->dev);
+               /* The 'src_comp' is set by rvtrace_register_component() */
+               conn->src_comp = NULL;
+               conn->dest_port = rep.port;
+               conn->dest_fwnode = of_fwnode_handle(rdev_node);
+               conn->dest_comp = rvtrace_find_by_fwnode(conn->dest_fwnode);
+               if (!conn->dest_comp) {
+                       ret = -EPROBE_DEFER;
+                       of_node_put(ep_node);
+               }
+
+               pdata->outconns[i] = conn;
+               i++;
+       }
+
+done:
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata)
+{
+       struct device_node *parent;
+       int ret = 0;
+
+       parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports");
+       if (!parent)
+               return 0;
+
+       pdata->nr_inconns = of_graph_get_endpoint_count(parent);
+       pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns,
+                                     sizeof(*pdata->inconns), GFP_KERNEL);
+       if (!pdata->inconns)
+               ret = -ENOMEM;
+
+       of_node_put(parent);
+       return ret;
+}
+
+static int rvtrace_platform_probe(struct platform_device *pdev)
+{
+       struct rvtrace_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct rvtrace_component *comp;
+       struct device_node *node;
+       struct resource *res;
+       int ret;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+       pdata->dev = dev;
+       pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       pdata->io_mem = true;
+       pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!pdata->base)
+               return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res);
+
+       pdata->bound_cpu = -1;
+       node = of_parse_phandle(dev_of_node(dev), "cpu", 0);
+       if (node) {
+               ret = of_cpu_node_to_id(node);
+               of_node_put(node);
+               if (ret < 0)
+                       return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node);
+               pdata->bound_cpu = ret;
+       }
+
+       /* Default control poll timeout */
+       pdata->control_poll_timeout_usecs = 10;
+
+       ret = rvtrace_of_parse_outconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse output connections\n");
+
+       ret = rvtrace_of_parse_inconns(pdata);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to parse input connections\n");
+
+       comp = rvtrace_register_component(pdata);
+       if (IS_ERR(comp))
+               return PTR_ERR(comp);
+
+       platform_set_drvdata(pdev, comp);
+       return 0;
+}
+
+static void rvtrace_platform_remove(struct platform_device *pdev)
+{
+       struct rvtrace_component *comp = platform_get_drvdata(pdev);
+
+       rvtrace_unregister_component(comp);
+}
+
+static const struct of_device_id rvtrace_platform_match[] = {
+       { .compatible = "riscv,trace-component" },
+       {}
+};
+
+struct platform_driver rvtrace_platform_driver = {
+       .driver = {
+               .name           = "rvtrace",
+               .of_match_table = rvtrace_platform_match,
+       },
+       .probe = rvtrace_platform_probe,
+       .remove = rvtrace_platform_remove,
+};
include/linux/rvtrace.h new file +272 -1
new file mode 100644
index 000000000000..04eb03e62601
@@ -0,0 +1,272 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#ifndef __LINUX_RVTRACE_H__
+#define __LINUX_RVTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+/* 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
+
+/* 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))
+
+/*
+ * Possible component implementation IDs discovered from DT or ACPI
+ * shared across the RISC-V trace drivers to infer trace parameters,
+ * quirks, and work-arounds. These component implementation IDs are
+ * internal to Linux and must not be exposed to user-space.
+ *
+ * The component implementation ID should be named as follows:
+ *    RVTRACE_COMPONENT_IMPID_<vendor>_<part>
+ */
+enum rvtrace_component_impid {
+       RVTRACE_COMPONENT_IMPID_UNKNOWN,
+       RVTRACE_COMPONENT_IMPID_MAX
+};
+
+/**
+ * struct rvtrace_connection - Representation of a physical connection between
+ * two RISC-V trace components.
+ * @src_port:    A connection's source port number.
+ * @src_fwnode:  Source component's fwnode handle..
+ * @src_comp:    Source component's pointer.
+ * @dest_port:   A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp:   Destination component's pointer.
+ */
+struct rvtrace_connection {
+       int src_port;
+       struct fwnode_handle *src_fwnode;
+       int dest_port;
+       struct fwnode_handle *dest_fwnode;
+       struct rvtrace_component *src_comp;
+       struct rvtrace_component *dest_comp;
+};
+
+/**
+ * struct rvtrace_platform_data - Platform-level data for a RISC-V trace component
+ * discovered from DT or ACPI.
+ * @dev:         Parent device.
+ * @impid:       Component implementation ID
+ * @io_mem:      Flag showing whether component registers are memory mapped.
+ * @base:        If io_mem == true then base address of the memory mapped registers.
+ * @read:        If io_mem == false then read register from the given "offset".
+ * @write:       If io_mem == false then write register to the given "offset".
+ * @bound_cpu:   CPU to which the component is bound. This should be -1 if
+ *               the component is not bound to any CPU. For encoder component
+ *               type this must not be -1.
+ * @nr_inconns:  Number of input connections.
+ * @inconns:     Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns:    Array of pointers to output connections.
+ */
+struct rvtrace_platform_data {
+       struct device *dev;
+
+       enum rvtrace_component_impid impid;
+
+       bool io_mem;
+       union {
+               void __iomem *base;
+               struct {
+                       u32 (*read)(struct rvtrace_platform_data *pdata,
+                                   u32 offset, bool relaxed);
+                       void (*write)(struct rvtrace_platform_data *pdata,
+                                     u32 val, u32 offset, bool relaxed);
+               };
+       };
+
+       int bound_cpu;
+
+       /* Delay in microseconds when polling control register bits */
+       int control_poll_timeout_usecs;
+
+       /*
+        * Platform driver must only populate empty pointer array without
+        * any actual input connections.
+        */
+       unsigned int nr_inconns;
+       struct rvtrace_connection **inconns;
+
+       /*
+        * Platform driver must fully populate pointer array with individual
+        * array elements pointing to actual output connections. The src_comp
+        * of each output connection is automatically updated at the time of
+        * registering component.
+        */
+       unsigned int nr_outconns;
+       struct rvtrace_connection **outconns;
+};
+
+static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl(pdata->base + offset);
+
+       return pdata->read(pdata, offset, false);
+}
+
+static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pdata, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               return readl_relaxed(pdata->base + offset);
+
+       return pdata->read(pdata, offset, true);
+}
+
+static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, false);
+}
+
+static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *pdata,
+                                          u32 val, u32 offset)
+{
+       if (likely(pdata->io_mem))
+               writel_relaxed(val, pdata->base + offset);
+       else
+               pdata->write(pdata, val, offset, true);
+}
+
+static inline bool rvtrace_is_source(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata)
+{
+       return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * 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
+ * pdata:    Pointer to underlying platform data
+ * id:       Details to match the component
+ * type_idx: Unique number based on component type
+ * dev:      Device instance
+ * ready:    Flag showing whether RISC-V trace driver was probed successfully
+ */
+struct rvtrace_component {
+       struct rvtrace_platform_data *pdata;
+       struct rvtrace_component_id id;
+       u32 type_idx;
+       struct device dev;
+       bool ready;
+};
+
+#define to_rvtrace_component(__dev)    container_of_const(__dev, struct rvtrace_component, dev)
+
+static inline void rvtrace_get_component(struct rvtrace_component *comp)
+{
+       get_device(&comp->dev);
+}
+
+static inline void rvtrace_put_component(struct rvtrace_component *comp)
+{
+       put_device(&comp->dev);
+}
+
+const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_component *comp,
+                                                   const struct rvtrace_component_id *ids);
+struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset,
+                    int bit, int bitval, int timeout);
+int rvtrace_enable_component(struct rvtrace_component *comp);
+int rvtrace_disable_component(struct rvtrace_component *comp);
+
+struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu);
+
+struct rvtrace_component *rvtrace_register_component(struct rvtrace_platform_data *pdata);
+void rvtrace_unregister_component(struct rvtrace_component *comp);
+
+/**
+ * struct rvtrace_driver - Representation of a RISC-V trace driver
+ * id_table: Table to match components handled by the driver
+ * probe:    Driver probe() function
+ * remove:   Driver remove() function
+ * driver:   Device driver instance
+ */
+struct rvtrace_driver {
+       const struct rvtrace_component_id *id_table;
+       int                     (*probe)(struct rvtrace_component *comp);
+       void                    (*remove)(struct rvtrace_component *comp);
+       struct device_driver    driver;
+};
+
+#define to_rvtrace_driver(__drv)   \
+       ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : NULL)
+
+extern struct platform_driver rvtrace_platform_driver;
+
+int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv);
+#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MODULE, driver)
+static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
+{
+       if (rtdrv)
+               driver_unregister(&rtdrv->driver);
+}
+
+#endif
--

2.43.0

Quoted 4 lines
Quoted 4 lines

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv

author
#11 lore raw

PATCH 3/11 rvtrace: Add functions to create/destroy a trace component path

In reply to Anup Patel

Trace needs to be configured on a chain of trace components which are connected to each other. These chain of components is also referred to as trace component path. Add functions to create/destroy a trace component path which will be later used by RISC-V trace perf support.

---
 drivers/hwtracing/rvtrace/rvtrace-core.c | 223 +++++++++++++++++++++++
 include/linux/rvtrace.h                  |  43 ++++-
 2 files changed, 264 insertions(+), 2 deletions(-)
drivers/hwtracing/rvtrace/rvtrace-core.c +223 -0
index 52ea931745fc..7013d50ca569 100644
@@ -194,6 +194,53 @@ int rvtrace_disable_component(struct rvtrace_component *comp)
 }
 EXPORT_SYMBOL_GPL(rvtrace_disable_component);
 
+static int __rvtrace_walk_output_components(struct rvtrace_component *comp,
+					    bool *stop, void *priv,
+					    int (*fn)(struct rvtrace_component *comp, bool *stop,
+						      struct rvtrace_connection *stop_conn,
+						      void *priv))
+{
+	struct rvtrace_connection *conn, *stop_conn = NULL;
+	struct rvtrace_platform_data *pdata = comp->pdata;
+	int i, ret;
+
+	for (i = 0; i < pdata->nr_outconns; i++) {
+		conn = pdata->outconns[i];
+		ret = __rvtrace_walk_output_components(conn->dest_comp, stop, priv, fn);
+		if (ret)
+			return ret;
+		if (*stop) {
+			stop_conn = conn;
+			break;
+		}
+	}
+
+	ret = fn(comp, stop, stop_conn, priv);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int rvtrace_walk_output_components(struct rvtrace_component *comp, void *priv,
+				   int (*fn)(struct rvtrace_component *comp, bool *stop,
+					     struct rvtrace_connection *stop_conn,
+					     void *priv))
+{
+	bool stop = false;
+	int ret;
+
+	if (!comp || !fn)
+		return -EINVAL;
+
+	mutex_lock(&rvtrace_mutex);
+	ret = __rvtrace_walk_output_components(comp, &stop, priv, fn);
+	mutex_unlock(&rvtrace_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rvtrace_walk_output_components);
+
 struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu)
 {
 	if (!cpu_present(cpu))
@@ -446,6 +493,182 @@ void rvtrace_unregister_component(struct rvtrace_component *comp)
 }
 EXPORT_SYMBOL_GPL(rvtrace_unregister_component);
 
+struct rvtrace_path_node {
+	struct list_head		head;
+	struct rvtrace_component	*comp;
+	struct rvtrace_connection	*conn;
+};
+
+struct rvtrace_component *rvtrace_path_source(struct rvtrace_path *path)
+{
+	struct rvtrace_path_node *node;
+
+	node = list_first_entry(&path->comp_list, struct rvtrace_path_node, head);
+	return node->comp;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_source);
+
+struct rvtrace_component *rvtrace_path_sink(struct rvtrace_path *path)
+{
+	struct rvtrace_path_node *node;
+
+	node = list_last_entry(&path->comp_list, struct rvtrace_path_node, head);
+	return node->comp;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_sink);
+
+static int rvtrace_assign_trace_id(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+	int trace_id;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+
+		if (!rtdrv->get_trace_id)
+			continue;
+
+		trace_id = rtdrv->get_trace_id(comp, path->mode);
+		if (trace_id > 0) {
+			path->trace_id = trace_id;
+			return 0;
+		} else if (trace_id < 0) {
+			return trace_id;
+		}
+	}
+
+	return 0;
+}
+
+static void rvtrace_unassign_trace_id(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+
+		if (!rtdrv->put_trace_id)
+			continue;
+
+		rtdrv->put_trace_id(comp, path->mode, path->trace_id);
+	}
+}
+
+static bool rvtrace_path_ready(struct rvtrace_path *path)
+{
+	struct rvtrace_path_node *node;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		if (!node->comp->ready)
+			return false;
+	}
+
+	return true;
+}
+
+struct build_path_walk_priv {
+	struct rvtrace_path		*path;
+	struct rvtrace_component	*sink;
+};
+
+static int build_path_walk_fn(struct rvtrace_component *comp, bool *stop,
+			      struct rvtrace_connection *stop_conn,
+			      void *priv)
+{
+	struct build_path_walk_priv *ppriv = priv;
+	struct rvtrace_path *path = ppriv->path;
+	struct rvtrace_path_node *node;
+
+	if ((!ppriv->sink && rvtrace_is_sink(comp->pdata)) ||
+	    (ppriv->sink && ppriv->sink == comp))
+		*stop = true;
+
+	if (*stop) {
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!path)
+			return -ENOMEM;
+		INIT_LIST_HEAD(&node->head);
+		rvtrace_get_component(comp);
+		node->comp = comp;
+		node->conn = stop_conn;
+		list_add(&node->head, &path->comp_list);
+	}
+
+	return 0;
+}
+
+static void rvtrace_release_path_nodes(struct rvtrace_path *path)
+{
+	struct rvtrace_path_node *node, *node1;
+
+	list_for_each_entry_safe(node, node1, &path->comp_list, head) {
+		list_del(&node->head);
+		rvtrace_put_component(node->comp);
+		kfree(node);
+	}
+}
+
+struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
+					 struct rvtrace_component *sink,
+					 enum rvtrace_component_mode mode)
+{
+	struct build_path_walk_priv priv;
+	struct rvtrace_path *path;
+	int ret = 0;
+
+	if (!source || mode >= RVTRACE_COMPONENT_MODE_MAX) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	path = kzalloc(sizeof(*path), GFP_KERNEL);
+	if (!path) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+	INIT_LIST_HEAD(&path->comp_list);
+	path->mode = mode;
+	path->trace_id = RVTRACE_INVALID_TRACE_ID;
+
+	priv.path = path;
+	priv.sink = sink;
+	ret = rvtrace_walk_output_components(source, &priv, build_path_walk_fn);
+	if (ret < 0)
+		goto err_release_path_nodes;
+
+	if (!rvtrace_path_ready(path)) {
+		ret = -EOPNOTSUPP;
+		goto err_release_path_nodes;
+	}
+
+	ret = rvtrace_assign_trace_id(path);
+	if (ret < 0)
+		goto err_release_path_nodes;
+
+	return path;
+
+err_release_path_nodes:
+	rvtrace_release_path_nodes(path);
+	kfree(path);
+err_out:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rvtrace_create_path);
+
+void rvtrace_destroy_path(struct rvtrace_path *path)
+{
+	rvtrace_unassign_trace_id(path);
+	rvtrace_release_path_nodes(path);
+	kfree(path);
+}
+EXPORT_SYMBOL_GPL(rvtrace_destroy_path);
+
 int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver *rtdrv)
 {
 	rtdrv->driver.owner = owner;
include/linux/rvtrace.h +41 -2

75 lines not on this page · raw message

Signature · 1 line
2.43.0
author
#12 lore raw

PATCH 4/11 rvtrace: Add functions to start/stop tracing on a component path

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
 drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
 include/linux/rvtrace.h                  |  6 ++++
 2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
 	}
 }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+	int ret;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+		if (!rtdrv->start)
+			continue;
+
+		ret = rtdrv->start(comp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+	int ret;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+		if (!rtdrv->stop)
+			continue;
+
+		ret = rtdrv->stop(comp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
 struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
 					 struct rvtrace_component *sink,
 					 enum rvtrace_component_mode mode)
include/linux/rvtrace.h +6 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
 					 struct rvtrace_component *sink,
 					 enum rvtrace_component_mode mode);
 void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
 /**
  * struct rvtrace_driver - Representation of a RISC-V trace driver
  * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
  * probe:        Driver probe() function
  * remove:       Driver remove() function
  * get_trace_id: Get/allocate a trace ID
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
  */
 struct rvtrace_driver {
 	const struct rvtrace_component_id *id_table;
+	int			(*start)(struct rvtrace_component *comp);
+	int			(*stop)(struct rvtrace_component *comp);
 	int			(*probe)(struct rvtrace_component *comp);
 	void			(*remove)(struct rvtrace_component *comp);
 	int			(*get_trace_id)(struct rvtrace_component *comp,
Signature · 1 line
2.43.0
#13 lore raw

Re: [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a component path

Quoted 89 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
  drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
  include/linux/rvtrace.h                  |  6 ++++
  2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
  	}
  }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+	int ret;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+		if (!rtdrv->start)
+			continue;
+
+		ret = rtdrv->start(comp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+	int ret;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+		if (!rtdrv->stop)
+			continue;
+
+		ret = rtdrv->stop(comp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
  struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
  					 struct rvtrace_component *sink,
  					 enum rvtrace_component_mode mode)
include/linux/rvtrace.h +4 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
  					 struct rvtrace_component *sink,
  					 enum rvtrace_component_mode mode);
  void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
  /**
   * struct rvtrace_driver - Representation of a RISC-V trace driver
   * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
   * probe:        Driver probe() function
   * remove:       Driver remove() function
   * get_trace_id: Get/allocate a trace ID

include/linux/rvtrace.h void rvtrace_destroy_path(struct rvtrace_path *path); · line 296

Quoted 8 lines
include/linux/rvtrace.h +2 -0
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
   */
  struct rvtrace_driver {
  	const struct rvtrace_component_id *id_table;
+	int			(*start)(struct rvtrace_component *comp);
+	int			(*stop)(struct rvtrace_component *comp);
  	int			(*probe)(struct rvtrace_component *comp);
  	void			(*remove)(struct rvtrace_component *comp);
  	int			(*get_trace_id)(struct rvtrace_component *comp,

I'd suggest add another function (*quiesce) or something like that. Trace components have a tr??Empty bit that indicates trace has been all flushed out. Also along the path when you do rvtrace_path_stop, you need to ensure the source has stopped and quiescent before beginning to stop the sink. Otherwise you'll get partial or corrupted trace. In essence, follow Control Interface Spec 11.3 Enabling and Disabling. FYI: my userspace driver:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L223

Bo

author
#14 lore raw

Re: [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a component path

Quoted 100 lines · Bo Gan
Quoted 89 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
  drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
  include/linux/rvtrace.h                  |  6 ++++
  2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
      }
  }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->start)
+                     continue;
+
+             ret = rtdrv->start(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->stop)
+                     continue;
+
+             ret = rtdrv->stop(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
  struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                       struct rvtrace_component *sink,
                                       enum rvtrace_component_mode mode)
include/linux/rvtrace.h +4 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                       struct rvtrace_component *sink,
                                       enum rvtrace_component_mode mode);
  void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
  /**
   * struct rvtrace_driver - Representation of a RISC-V trace driver
   * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
   * probe:        Driver probe() function
   * remove:       Driver remove() function
   * get_trace_id: Get/allocate a trace ID

include/linux/rvtrace.h void rvtrace_destroy_path(struct rvtrace_path *path); · line 296

Quoted 8 lines
include/linux/rvtrace.h +2 -0
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
   */
  struct rvtrace_driver {
      const struct rvtrace_component_id *id_table;
+     int                     (*start)(struct rvtrace_component *comp);
+     int                     (*stop)(struct rvtrace_component *comp);
      int                     (*probe)(struct rvtrace_component *comp);
      void                    (*remove)(struct rvtrace_component *comp);
      int                     (*get_trace_id)(struct rvtrace_component *comp,
Quoted 7 lines

I'd suggest add another function (*quiesce) or something like that. Trace components have a tr??Empty bit that indicates trace has been all flushed out. Also along the path when you do rvtrace_path_stop, you need to ensure the source has stopped and quiescent before beginning to stop the sink. Otherwise you'll get partial or corrupted trace. In essence, follow Control Interface Spec 11.3 Enabling and Disabling. FYI: my userspace driver:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L223

It's better to add functions on a need basis rather than adding it now without any potential user.

Regards,
Anu

#15 lore raw

Re: [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a component path

Quoted 109 lines · Anup Patel
Quoted 100 lines · Bo Gan
Quoted 89 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
   drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
   include/linux/rvtrace.h                  |  6 ++++
   2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
       }
   }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->start)
+                     continue;
+
+             ret = rtdrv->start(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->stop)
+                     continue;
+
+             ret = rtdrv->stop(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
   struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                        struct rvtrace_component *sink,
                                        enum rvtrace_component_mode mode)
include/linux/rvtrace.h +4 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                        struct rvtrace_component *sink,
                                        enum rvtrace_component_mode mode);
   void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
   /**
    * struct rvtrace_driver - Representation of a RISC-V trace driver
    * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
    * probe:        Driver probe() function
    * remove:       Driver remove() function
    * get_trace_id: Get/allocate a trace ID

include/linux/rvtrace.h void rvtrace_destroy_path(struct rvtrace_path *path); · line 296

Quoted 8 lines
include/linux/rvtrace.h +2 -0
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
    */
   struct rvtrace_driver {
       const struct rvtrace_component_id *id_table;
+     int                     (*start)(struct rvtrace_component *comp);
+     int                     (*stop)(struct rvtrace_component *comp);
       int                     (*probe)(struct rvtrace_component *comp);
       void                    (*remove)(struct rvtrace_component *comp);
       int                     (*get_trace_id)(struct rvtrace_component *comp,
Quoted 7 lines

I'd suggest add another function (*quiesce) or something like that. Trace components have a tr??Empty bit that indicates trace has been all flushed out. Also along the path when you do rvtrace_path_stop, you need to ensure the source has stopped and quiescent before beginning to stop the sink. Otherwise you'll get partial or corrupted trace. In essence, follow Control Interface Spec 11.3 Enabling and Disabling. FYI: my userspace driver:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L223

Quoted 5 lines

It's better to add functions on a need basis rather than adding it now without any potential user.

Regards,
Anu

Hi Anup, my previous comment also applies to your current use case where you have encoder->RAM sink directly connected together. Having a longer path, e.g., funnels in between makes it worse. The driver needs to poll the empty bit tr??Empty (bit 3) of the control register to check if trace has been completely flushed. Otherwise, you get a partial trace, possibly with last few messages missing or truncated. So, yes, there's really a need to do so.

Bo

#16 lore raw

Re: [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a component path

Hi Bo,

Quoted 117 lines · Bo Gan
Quoted 109 lines · Anup Patel
Quoted 100 lines · Bo Gan
Quoted 89 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
   drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
   include/linux/rvtrace.h                  |  6 ++++
   2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
       }
   }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->start)
+                     continue;
+
+             ret = rtdrv->start(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->stop)
+                     continue;
+
+             ret = rtdrv->stop(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
   struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                        struct rvtrace_component *sink,
                                        enum rvtrace_component_mode mode)
include/linux/rvtrace.h +4 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                        struct rvtrace_component *sink,
                                        enum rvtrace_component_mode mode);
   void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
   /**
    * struct rvtrace_driver - Representation of a RISC-V trace driver
    * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
    * probe:        Driver probe() function
    * remove:       Driver remove() function
    * get_trace_id: Get/allocate a trace ID

include/linux/rvtrace.h void rvtrace_destroy_path(struct rvtrace_path *path); · line 296

Quoted 8 lines
include/linux/rvtrace.h +2 -0
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
    */
   struct rvtrace_driver {
       const struct rvtrace_component_id *id_table;
+     int                     (*start)(struct rvtrace_component *comp);
+     int                     (*stop)(struct rvtrace_component *comp);
       int                     (*probe)(struct rvtrace_component *comp);
       void                    (*remove)(struct rvtrace_component *comp);
       int                     (*get_trace_id)(struct rvtrace_component *comp,
Quoted 7 lines

I'd suggest add another function (*quiesce) or something like that. Trace components have a tr??Empty bit that indicates trace has been all flushed out. Also along the path when you do rvtrace_path_stop, you need to ensure the source has stopped and quiescent before beginning to stop the sink. Otherwise you'll get partial or corrupted trace. In essence, follow Control Interface Spec 11.3 Enabling and Disabling. FYI: my userspace driver:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L223

Quoted 5 lines

It's better to add functions on a need basis rather than adding it now without any potential user.

Regards,
Anu

Quoted 6 lines

Hi Anup, my previous comment also applies to your current use case where you have encoder->RAM sink directly connected together. Having a longer path, e.g., funnels in between makes it worse. The driver needs to poll the empty bit tr??Empty (bit 3) of the control register to check if trace has been completely flushed. Otherwise, you get a partial trace, possibly with last few messages missing or truncated. So, yes, there's really a need to do so.

I think this can also be implemented in the component's 'stop' callback.

Quoted 2 lines

Bo

#17 lore raw

Re: [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a component path

Hi Mayuresh,

Quoted 120 lines · Mayuresh Chitale

Hi Bo,

Quoted 117 lines · Bo Gan
Quoted 114 lines · Anup Patel
Quoted 100 lines · Bo Gan
Quoted 89 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

The perf driver framework needs to be able to start / stop all components in a trace component path during its operation. Add rvtrace_path_start() and rvtrace_path_stop() functions for this purpose.

---
    drivers/hwtracing/rvtrace/rvtrace-core.c | 44 ++++++++++++++++++++++++
    include/linux/rvtrace.h                  |  6 ++++
    2 files changed, 50 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +44 -0
index 7013d50ca569..109be40d4b24 100644
@@ -614,6 +614,50 @@ static void rvtrace_release_path_nodes(struct rvtrace_path *path)
        }
    }
 
+int rvtrace_path_start(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->start)
+                     continue;
+
+             ret = rtdrv->start(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_start);
+
+int rvtrace_path_stop(struct rvtrace_path *path)
+{
+     const struct rvtrace_driver *rtdrv;
+     struct rvtrace_component *comp;
+     struct rvtrace_path_node *node;
+     int ret;
+
+     list_for_each_entry(node, &path->comp_list, head) {
+             comp = node->comp;
+             rtdrv = to_rvtrace_driver(comp->dev.driver);
+             if (!rtdrv->stop)
+                     continue;
+
+             ret = rtdrv->stop(comp);
+             if (ret)
+                     return ret;
+     }
+
+     return 0;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_stop);
+
    struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                         struct rvtrace_component *sink,
                                         enum rvtrace_component_mode mode)
include/linux/rvtrace.h +4 -0
index f2174f463a69..e7bd335d388f 100644
@@ -273,10 +273,14 @@ struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
                                         struct rvtrace_component *sink,
                                         enum rvtrace_component_mode mode);
    void rvtrace_destroy_path(struct rvtrace_path *path);
+int rvtrace_path_start(struct rvtrace_path *path);
+int rvtrace_path_stop(struct rvtrace_path *path);
 
    /**
     * struct rvtrace_driver - Representation of a RISC-V trace driver
     * id_table: Table to match components handled by the driver
+ * start:        Callback to start tracing
+ * stop:         Callback to stop tracing
     * probe:        Driver probe() function
     * remove:       Driver remove() function
     * get_trace_id: Get/allocate a trace ID

include/linux/rvtrace.h void rvtrace_destroy_path(struct rvtrace_path *path); · line 296

Quoted 8 lines
include/linux/rvtrace.h +2 -0
@@ -285,6 +289,8 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
     */
    struct rvtrace_driver {
        const struct rvtrace_component_id *id_table;
+     int                     (*start)(struct rvtrace_component *comp);
+     int                     (*stop)(struct rvtrace_component *comp);
        int                     (*probe)(struct rvtrace_component *comp);
        void                    (*remove)(struct rvtrace_component *comp);
        int                     (*get_trace_id)(struct rvtrace_component *comp,
Quoted 7 lines

I'd suggest add another function (*quiesce) or something like that. Trace components have a tr??Empty bit that indicates trace has been all flushed out. Also along the path when you do rvtrace_path_stop, you need to ensure the source has stopped and quiescent before beginning to stop the sink. Otherwise you'll get partial or corrupted trace. In essence, follow Control Interface Spec 11.3 Enabling and Disabling. FYI: my userspace driver:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L223

It's better to add functions on a need basis rather than adding it now without any potential user.

Regards,
Anu

Quoted 8 lines
Quoted 6 lines

Hi Anup, my previous comment also applies to your current use case where you have encoder->RAM sink directly connected together. Having a longer path, e.g., funnels in between makes it worse. The driver needs to poll the empty bit tr??Empty (bit 3) of the control register to check if trace has been completely flushed. Otherwise, you get a partial trace, possibly with last few messages missing or truncated. So, yes, there's really a need to do so.

I think this can also be implemented in the component's 'stop' callback.

Sure, that also works.

Quoted 2 lines
Quoted 2 lines

Bo

Bo

author
#18 lore raw

PATCH 5/11 rvtrace: Add trace encoder driver

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
 drivers/hwtracing/rvtrace/Kconfig           |   7 ++
 drivers/hwtracing/rvtrace/Makefile          |   1 +
 drivers/hwtracing/rvtrace/rvtrace-encoder.c | 107 ++++++++++++++++++++
 3 files changed, 115 insertions(+)
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-encoder.c
drivers/hwtracing/rvtrace/Kconfig +7 -0
index f8f6feea1953..ba35c05f3f54 100644
@@ -14,3 +14,10 @@ menuconfig RVTRACE
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called rvtrace.
+
+config RVTRACE_ENCODER
+	tristate "RISC-V Trace Encoder driver"
+	depends on RVTRACE
+	default y
+	help
+	  This driver provides support for RISC-V Trace Encoder component.
drivers/hwtracing/rvtrace/Makefile +1 -0
index 988525a379cf..f320693a1fc5 100644
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_RVTRACE) += rvtrace.o
 rvtrace-y := rvtrace-core.o rvtrace-platform.o
+obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +107 -0

107 lines not on this page · raw message

Signature · 1 line
2.43.0
#19 lore raw

Re: [PATCH 05/11] rvtrace: Add trace encoder driver

Quoted 98 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
  drivers/hwtracing/rvtrace/Kconfig           |   7 ++
  drivers/hwtracing/rvtrace/Makefile          |   1 +
  drivers/hwtracing/rvtrace/rvtrace-encoder.c | 107 ++++++++++++++++++++
  3 files changed, 115 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/rvtrace-encoder.c

drivers/hwtracing/rvtrace/Kconfig +7 -0
index f8f6feea1953..ba35c05f3f54 100644
@@ -14,3 +14,10 @@ menuconfig RVTRACE
 
  	  To compile this driver as a module, choose M here: the module
  	  will be called rvtrace.
+
+config RVTRACE_ENCODER
+	tristate "RISC-V Trace Encoder driver"
+	depends on RVTRACE
+	default y
+	help
+	  This driver provides support for RISC-V Trace Encoder component.
drivers/hwtracing/rvtrace/Makefile +1 -0
index 988525a379cf..f320693a1fc5 100644
@@ -2,3 +2,4 @@
 
  obj-$(CONFIG_RVTRACE) += rvtrace.o
  rvtrace-y := rvtrace-core.o rvtrace-platform.o
+obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +53 -0
new file mode 100644
index 000000000000..45d1c5b12c51
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+#define RVTRACE_COMPONENT_CTRL_ITRACE_MASK	0x1
+#define RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT	2
+#define RVTRACE_COMPONENT_CTRL_INSTMODE_MASK	0x7
+#define RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT	4
+
+static int rvtrace_encoder_start(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
+	rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 1,
+				comp->pdata->control_poll_timeout_usecs);
+}
+
+static int rvtrace_encoder_stop(struct rvtrace_component *comp)
+{
+	u32 val;
+
+	val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+	val &= ~BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
+	rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+	return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+				RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 0,
+				comp->pdata->control_poll_timeout_usecs);
+}
+
+static void rvtrace_encoder_setmode(struct rvtrace_component *comp, u32 mode)
+{
+	u32 val;
+
+	val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+	val |= (mode << RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT);
+	rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+}
+
+static int rvtrace_encoder_probe(struct rvtrace_component *comp)

drivers/hwtracing/rvtrace/rvtrace-encoder.c line 63

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +10 -0
+{
+	int ret;
+
+	rvtrace_encoder_setmode(comp, 0x6);
+	ret = rvtrace_enable_component(comp);
+	if (ret)
+		return dev_err_probe(&comp->dev, ret, "failed to enable encoder.\n");
+
+	return 0;
+}

Trace components needs proper probing before use. Refer to Control Interface
Spec: https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf
Chapter 5. This versioning applies to all components, e.g., sinks/funnels...
The driver should check the HW impl version with what it supports, and
rejects registering the component(s) if not supported. Chapter 5. has more
details.

Quoted 34 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +34 -0
+
+static void rvtrace_encoder_remove(struct rvtrace_component *comp)
+{
+	int ret;
+
+	ret = rvtrace_disable_component(comp);
+	if (ret)
+		dev_err(&comp->dev, "failed to disable encoder.\n");
+}
+
+static struct rvtrace_component_id rvtrace_encoder_ids[] = {
+	{ .type = RVTRACE_COMPONENT_TYPE_ENCODER,
+	  .version = rvtrace_component_mkversion(1, 0), },
+	{},
+};
+
+static struct rvtrace_driver rvtrace_encoder_driver = {
+	.id_table = rvtrace_encoder_ids,
+	.start = rvtrace_encoder_start,
+	.stop = rvtrace_encoder_stop,
+	.probe = rvtrace_encoder_probe,
+	.remove = rvtrace_encoder_remove,
+	.driver = {
+		.name = "rvtrace-encoder",
+	},
+};
+
+static int __init rvtrace_encoder_init(void)
+{
+	return rvtrace_register_driver(&rvtrace_encoder_driver);
+}
+
+static void __exit rvtrace_encoder_exit(void)
+{

drivers/hwtracing/rvtrace/rvtrace-encoder.c line 107

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +10 -0
+	rvtrace_unregister_driver(&rvtrace_encoder_driver);
+}
+
+module_init(rvtrace_encoder_init);
+module_exit(rvtrace_encoder_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mayuresh Chitale <[email protected]>");
+MODULE_DESCRIPTION("RISC-V Trace Encoder Driver");
+MODULE_LICENSE("GPL");

Bo

#20 lore raw

Re: [PATCH 05/11] rvtrace: Add trace encoder driver

Quoted 105 lines · Bo Gan
Quoted 104 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
  drivers/hwtracing/rvtrace/Kconfig           |   7 ++ drivers/hwtracing/rvtrace/Makefile          |   1 + drivers/hwtracing/rvtrace/rvtrace-encoder.c | 107 ++++++++++++++++++++

  3 files changed, 115 insertions(+)

  create mode 100644 drivers/hwtracing/rvtrace/rvtrace-encoder.c

drivers/hwtracing/rvtrace/Kconfig +0 -0
index f8f6feea1953..ba35c05f3f54 100644
@@ -14,3 +14,10 @@ menuconfig RVTRACE

        To compile this driver as a module, choose M here: the module will be called rvtrace.

drivers/hwtracing/rvtrace/Kconfig +7 -0
+
+config RVTRACE_ENCODER
+    tristate "RISC-V Trace Encoder driver"
+    depends on RVTRACE
+    default y
+    help
+      This driver provides support for RISC-V Trace Encoder component.
drivers/hwtracing/rvtrace/Makefile +0 -0
index 988525a379cf..f320693a1fc5 100644
@@ -2,3 +2,4 @@

  obj-$(CONFIG_RVTRACE) += rvtrace.o
  rvtrace-y := rvtrace-core.o rvtrace-platform.o

drivers/hwtracing/rvtrace/Makefile +1 -0
+obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +61 -0
new file mode 100644
index 000000000000..45d1c5b12c51
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+
+#define RVTRACE_COMPONENT_CTRL_ITRACE_MASK    0x1
+#define RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT    2
+#define RVTRACE_COMPONENT_CTRL_INSTMODE_MASK    0x7
+#define RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT    4
+
+static int rvtrace_encoder_start(struct rvtrace_component *comp)
+{
+    u32 val;
+
+    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+    val |= BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
+    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+    return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 1,
+                comp->pdata->control_poll_timeout_usecs);
+}
+
+static int rvtrace_encoder_stop(struct rvtrace_component *comp)
+{
+    u32 val;
+
+    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+    val &= ~BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
+    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+    return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
+                RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 0,
+                comp->pdata->control_poll_timeout_usecs);
+}
+
+static void rvtrace_encoder_setmode(struct rvtrace_component *comp, u32 mode)
+{
+    u32 val;
+
+    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
+    val |= (mode << RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT);
+    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
+}
+
+static int rvtrace_encoder_probe(struct rvtrace_component *comp)
+{
+    int ret;
+
+    rvtrace_encoder_setmode(comp, 0x6);
+    ret = rvtrace_enable_component(comp);
+    if (ret)
+        return dev_err_probe(&comp->dev, ret, "failed to enable encoder.\n");
+
Quoted 9 lines

drivers/hwtracing/rvtrace/rvtrace-encoder.c line 63

Quoted 2 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +2 -0
+    return 0;
+}

Trace components needs proper probing before use. Refer to Control Interface
Spec: https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf
Chapter 5. This versioning applies to all components, e.g., sinks/funnels...
The driver should check the HW impl version with what it supports, and
rejects registering the component(s) if not supported. Chapter 5. has more
details.

Forgot to mention another thing: you also need to follow Control Interface Spec Chapter 11.2 Reset and Discovery to toggle tr??Active. Do not access any other registers or set any other bits in tr??Control before properly enable tr??Active. My userspace driver does the reset as:
https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/device.py#L57

Your HW IP might ignore tr??Active bit and the trace components are always clocked, and that's probably why you don't see any issue without proper resetting. However, on my p550 (EIC7700), I adopted your code to a v6.6 tree, boot, and load it as module. The core immediately hangs, very likely due to it tries to set tr??Control bits without proper resetting. You should expect other IPs that implement clock gating and strictly follow the Spec. Thanks.

Quoted 38 lines
Quoted 38 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +38 -0
+
+static void rvtrace_encoder_remove(struct rvtrace_component *comp)
+{
+    int ret;
+
+    ret = rvtrace_disable_component(comp);
+    if (ret)
+        dev_err(&comp->dev, "failed to disable encoder.\n");
+}
+
+static struct rvtrace_component_id rvtrace_encoder_ids[] = {
+    { .type = RVTRACE_COMPONENT_TYPE_ENCODER,
+      .version = rvtrace_component_mkversion(1, 0), },
+    {},
+};
+
+static struct rvtrace_driver rvtrace_encoder_driver = {
+    .id_table = rvtrace_encoder_ids,
+    .start = rvtrace_encoder_start,
+    .stop = rvtrace_encoder_stop,
+    .probe = rvtrace_encoder_probe,
+    .remove = rvtrace_encoder_remove,
+    .driver = {
+        .name = "rvtrace-encoder",
+    },
+};
+
+static int __init rvtrace_encoder_init(void)
+{
+    return rvtrace_register_driver(&rvtrace_encoder_driver);
+}
+
+static void __exit rvtrace_encoder_exit(void)
+{
+    rvtrace_unregister_driver(&rvtrace_encoder_driver);
+}
+
+module_init(rvtrace_encoder_init);
Quoted 8 lines

drivers/hwtracing/rvtrace/rvtrace-encoder.c line 107

Quoted 6 lines
drivers/hwtracing/rvtrace/rvtrace-encoder.c new file +6 -0
+module_exit(rvtrace_encoder_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mayuresh Chitale <[email protected]>");
+MODULE_DESCRIPTION("RISC-V Trace Encoder Driver");
+MODULE_LICENSE("GPL");

Bo

Bo

A message that is not in the archive (sent off-list or not yet received): CAN37VV7uBkRzYsQcgGtw_iFg=za91OH7_1OSJ+b8eeuCzL5iDw@mail.gmail.com

#22 lore raw

Re: [PATCH 05/11] rvtrace: Add trace encoder driver

Hi Mayuresh,

I overlooked the ordering of your rvtrace_component_reset and registering on rvtrace bus. Yes, you are doing proper reset, so please ignore my previous msg. Sorry for the noise. Now back to figure out why my hacked version doesn't work on my p550...

On a side note, it seems that there's something wrong with your email client? I don't see your reply on the mailing-list:
https://lore.kernel.org/all/[email protected]/
And your reply mail appears not in plain text mode.

Quoted 176 lines · Mayuresh Chitale

On Wed, Oct 8, 2025 at 2:19 PM Bo Gan <[email protected] <mailto:[email protected]>> wrote:

    On 10/7/25 00:09, Bo Gan wrote:
     > On 10/1/25 23:07, Anup Patel wrote:
     >> From: Mayuresh Chitale <[email protected] <mailto:[email protected]>>
     >>
     >> Add initial implementation of RISC-V trace encoder driver. The encoder
     >> is defined in the RISC-V Trace Control Interface specification.
     >>
     >> Co-developed-by: Anup Patel <[email protected] <mailto:[email protected]>>
     >> Signed-off-by: Anup Patel <[email protected] <mailto:[email protected]>>
     >> Signed-off-by: Mayuresh Chitale <[email protected] <mailto:[email protected]>>
     >> ---
     >>   drivers/hwtracing/rvtrace/Kconfig           |   7 ++
     >>   drivers/hwtracing/rvtrace/Makefile          |   1 +
     >>   drivers/hwtracing/rvtrace/rvtrace-encoder.c | 107 ++++++++++++++++++++
     >>   3 files changed, 115 insertions(+)
     >>   create mode 100644 drivers/hwtracing/rvtrace/rvtrace-encoder.c
     >>
     >> diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/Kconfig
     >> index f8f6feea1953..ba35c05f3f54 100644
     >> --- a/drivers/hwtracing/rvtrace/Kconfig
     >> +++ b/drivers/hwtracing/rvtrace/Kconfig
     >> @@ -14,3 +14,10 @@ menuconfig RVTRACE
     >>         To compile this driver as a module, choose M here: the module
     >>         will be called rvtrace.
     >> +
     >> +config RVTRACE_ENCODER
     >> +    tristate "RISC-V Trace Encoder driver"
     >> +    depends on RVTRACE
     >> +    default y
     >> +    help
     >> +      This driver provides support for RISC-V Trace Encoder component.
     >> diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace/Makefile
     >> index 988525a379cf..f320693a1fc5 100644
     >> --- a/drivers/hwtracing/rvtrace/Makefile
     >> +++ b/drivers/hwtracing/rvtrace/Makefile
     >> @@ -2,3 +2,4 @@
     >>   obj-$(CONFIG_RVTRACE) += rvtrace.o
     >>   rvtrace-y := rvtrace-core.o rvtrace-platform.o
     >> +obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
     >> diff --git a/drivers/hwtracing/rvtrace/rvtrace-encoder.c b/drivers/hwtracing/rvtrace/rvtrace-encoder.c
     >> new file mode 100644
     >> index 000000000000..45d1c5b12c51
     >> --- /dev/null
     >> +++ b/drivers/hwtracing/rvtrace/rvtrace-encoder.c
     >> @@ -0,0 +1,107 @@
     >> +// SPDX-License-Identifier: GPL-2.0
     >> +/*
     >> + * Copyright (c) 2025 Ventana Micro Systems Inc.
     >> + */
     >> +
     >> +#include <linux/device.h>
     >> +#include <linux/io.h>
     >> +#include <linux/of.h>
     >> +#include <linux/of_graph.h>
     >> +#include <linux/platform_device.h>
     >> +#include <linux/property.h>
     >> +#include <linux/rvtrace.h>
     >> +#include <linux/types.h>
     >> +
     >> +#define RVTRACE_COMPONENT_CTRL_ITRACE_MASK    0x1
     >> +#define RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT    2
     >> +#define RVTRACE_COMPONENT_CTRL_INSTMODE_MASK    0x7
     >> +#define RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT    4
     >> +
     >> +static int rvtrace_encoder_start(struct rvtrace_component *comp)
     >> +{
     >> +    u32 val;
     >> +
     >> +    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +    val |= BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
     >> +    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +    return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
     >> +                RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 1,
     >> +                comp->pdata->control_poll_timeout_usecs);
     >> +}
     >> +
     >> +static int rvtrace_encoder_stop(struct rvtrace_component *comp)
     >> +{
     >> +    u32 val;
     >> +
     >> +    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +    val &= ~BIT(RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT);
     >> +    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +    return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET,
     >> +                RVTRACE_COMPONENT_CTRL_ITRACE_SHIFT, 0,
     >> +                comp->pdata->control_poll_timeout_usecs);
     >> +}
     >> +
     >> +static void rvtrace_encoder_setmode(struct rvtrace_component *comp, u32 mode)
     >> +{
     >> +    u32 val;
     >> +
     >> +    val = rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +    val |= (mode << RVTRACE_COMPONENT_CTRL_INSTMODE_SHIFT);
     >> +    rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET);
     >> +}
     >> +
     >> +static int rvtrace_encoder_probe(struct rvtrace_component *comp)
     >> +{
     >> +    int ret;
     >> +
     >> +    rvtrace_encoder_setmode(comp, 0x6);
     >> +    ret = rvtrace_enable_component(comp);
     >> +    if (ret)
     >> +        return dev_err_probe(&comp->dev, ret, "failed to enable encoder.\n");
     >> +
     >> +    return 0;
     >> +}
     > Trace components needs proper probing before use. Refer to Control Interface
     > Spec: https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf <https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf>
     > Chapter 5. This versioning applies to all components, e.g., sinks/funnels...
     > The driver should check the HW impl version with what it supports, and
     > rejects registering the component(s) if not supported. Chapter 5. has more
     > details.
     >
    Forgot to mention another thing: you also need to follow Control Interface
    Spec Chapter 11.2 Reset and Discovery to toggle tr??Active. Do not access
    any other registers or set any other bits in tr??Control before properly
    enable tr??Active. My userspace driver does the reset as:
    https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/device.py#L57 <https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/device.py#L57>

Actually reset is done when a component is registered (rvtrace_register_component) and it is enabled when it gets probed (rvtrace_enable_component).  I will incorporate the other changes in V2.

    Your HW IP might ignore tr??Active bit and the trace components are always
    clocked, and that's probably why you don't see any issue without proper
    resetting. However, on my p550 (EIC7700), I adopted your code to a v6.6
    tree, boot, and load it as module. The core immediately hangs, very likely
    due to it tries to set tr??Control bits without proper resetting. You
    should expect other IPs that implement clock gating and strictly follow
    the Spec. Thanks.

     >> +
     >> +static void rvtrace_encoder_remove(struct rvtrace_component *comp)
     >> +{
     >> +    int ret;
     >> +
     >> +    ret = rvtrace_disable_component(comp);
     >> +    if (ret)
     >> +        dev_err(&comp->dev, "failed to disable encoder.\n");
     >> +}
     >> +
     >> +static struct rvtrace_component_id rvtrace_encoder_ids[] = {
     >> +    { .type = RVTRACE_COMPONENT_TYPE_ENCODER,
     >> +      .version = rvtrace_component_mkversion(1, 0), },
     >> +    {},
     >> +};
     >> +
     >> +static struct rvtrace_driver rvtrace_encoder_driver = {
     >> +    .id_table = rvtrace_encoder_ids,
     >> +    .start = rvtrace_encoder_start,
     >> +    .stop = rvtrace_encoder_stop,
     >> +    .probe = rvtrace_encoder_probe,
     >> +    .remove = rvtrace_encoder_remove,
     >> +    .driver = {
     >> +        .name = "rvtrace-encoder",
     >> +    },
     >> +};
     >> +
     >> +static int __init rvtrace_encoder_init(void)
     >> +{
     >> +    return rvtrace_register_driver(&rvtrace_encoder_driver);
     >> +}
     >> +
     >> +static void __exit rvtrace_encoder_exit(void)
     >> +{
     >> +    rvtrace_unregister_driver(&rvtrace_encoder_driver);
     >> +}
     >> +
     >> +module_init(rvtrace_encoder_init);
     >> +module_exit(rvtrace_encoder_exit);
     >> +
Quoted 9 lines
     >> +/* Module information */
     >> +MODULE_AUTHOR("Mayuresh Chitale <[email protected] <mailto:[email protected]>>");
     >> +MODULE_DESCRIPTION("RISC-V Trace Encoder Driver");
     >> +MODULE_LICENSE("GPL");
     >
     > Bo

    Bo

Bo

author
#23 lore raw

PATCH 6/11 rvtrace: Add function to copy into perf AUX buffer

In reply to Anup Patel

The RISC-V trace ramsink will need a mechanism to copy trace data into the perf AUX buffer. Add rvtrace_path_copyto_auxbuf() function and corresponding trace driver callback copyto_auxbuf() for this purpose.

---
 drivers/hwtracing/rvtrace/rvtrace-core.c | 22 ++++++++++++++++++++++
 include/linux/rvtrace.h                  | 21 +++++++++++++++++++++
 2 files changed, 43 insertions(+)
drivers/hwtracing/rvtrace/rvtrace-core.c +22 -0
index 109be40d4b24..e874899c8b43 100644
@@ -658,6 +658,28 @@ int rvtrace_path_stop(struct rvtrace_path *path)
 }
 EXPORT_SYMBOL_GPL(rvtrace_path_stop);
 
+int rvtrace_path_copyto_auxbuf(struct rvtrace_path *path,
+			       struct rvtrace_perf_auxbuf *buf,
+			       size_t *bytes_copied)
+{
+	const struct rvtrace_driver *rtdrv;
+	struct rvtrace_component *comp;
+	struct rvtrace_path_node *node;
+
+	list_for_each_entry(node, &path->comp_list, head) {
+		comp = node->comp;
+		rtdrv = to_rvtrace_driver(comp->dev.driver);
+		if (!rtdrv->copyto_auxbuf)
+			continue;
+
+		*bytes_copied = rtdrv->copyto_auxbuf(comp, buf);
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(rvtrace_path_copyto_auxbuf);
+
 struct rvtrace_path *rvtrace_create_path(struct rvtrace_component *source,
 					 struct rvtrace_component *sink,
 					 enum rvtrace_component_mode mode)
include/linux/rvtrace.h +21 -0
index e7bd335d388f..17d2fd9234c2 100644
@@ -276,9 +276,28 @@ void rvtrace_destroy_path(struct rvtrace_path *path);
 int rvtrace_path_start(struct rvtrace_path *path);
 int rvtrace_path_stop(struct rvtrace_path *path);
 
+/**
+ * struct rvtrace_perf_auxbuf - Representation of the perf AUX buffer
+ * @length:   size of the AUX buffer
+ * @nr_pages: number of pages of the AUX buffer
+ * @base:     start address of AUX buffer
+ * @pos:      position in the AUX buffer to commit traced data
+ */
+struct rvtrace_perf_auxbuf {
+	size_t length;
+	int nr_pages;
+	void *base;
+	long pos;
+};
+
+int rvtrace_path_copyto_auxbuf(struct rvtrace_path *path,
+			       struct rvtrace_perf_auxbuf *buf,
+			       size_t *bytes_copied);
+
 /**
  * struct rvtrace_driver - Representation of a RISC-V trace driver
  * id_table: Table to match components handled by the driver
+ * copyto_auxbuf:Callback to copy data into perf AUX buffer
  * start:        Callback to start tracing
  * stop:         Callback to stop tracing
  * probe:        Driver probe() function
@@ -289,6 +308,8 @@ int rvtrace_path_stop(struct rvtrace_path *path);
  */
 struct rvtrace_driver {
 	const struct rvtrace_component_id *id_table;
+	size_t			(*copyto_auxbuf)(struct rvtrace_component *comp,
+						 struct rvtrace_perf_auxbuf *buf);
 	int			(*start)(struct rvtrace_component *comp);
 	int			(*stop)(struct rvtrace_component *comp);
 	int			(*probe)(struct rvtrace_component *comp);
Signature · 1 line
2.43.0
author
#24 lore raw

PATCH 7/11 rvtrace: Add trace ramsink driver

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
 drivers/hwtracing/rvtrace/Kconfig           |   8 +
 drivers/hwtracing/rvtrace/Makefile          |   1 +
 drivers/hwtracing/rvtrace/rvtrace-ramsink.c | 198 ++++++++++++++++++++
 3 files changed, 207 insertions(+)
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c
drivers/hwtracing/rvtrace/Kconfig +8 -0
index ba35c05f3f54..aef7e9989165 100644
@@ -21,3 +21,11 @@ config RVTRACE_ENCODER
 	default y
 	help
 	  This driver provides support for RISC-V Trace Encoder component.
+
+config RVTRACE_RAMSINK
+	tristate "RISC-V Trace Ramsink driver"
+	depends on RVTRACE
+	default y
+	help
+	  This driver provides support for Risc-V E-Trace Ramsink
+	  component.
drivers/hwtracing/rvtrace/Makefile +1 -0
index f320693a1fc5..122e575da9fb 100644
@@ -3,3 +3,4 @@
 obj-$(CONFIG_RVTRACE) += rvtrace.o
 rvtrace-y := rvtrace-core.o rvtrace-platform.o
 obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
+obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +198 -0

198 lines not on this page · raw message

Signature · 1 line
2.43.0
#25 lore raw

Re: [PATCH 07/11] rvtrace: Add trace ramsink driver

Quoted 150 lines · Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
  drivers/hwtracing/rvtrace/Kconfig           |   8 +
  drivers/hwtracing/rvtrace/Makefile          |   1 +
  drivers/hwtracing/rvtrace/rvtrace-ramsink.c | 198 ++++++++++++++++++++
  3 files changed, 207 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c

drivers/hwtracing/rvtrace/Kconfig +8 -0
index ba35c05f3f54..aef7e9989165 100644
@@ -21,3 +21,11 @@ config RVTRACE_ENCODER
  	default y
  	help
  	  This driver provides support for RISC-V Trace Encoder component.
+
+config RVTRACE_RAMSINK
+	tristate "RISC-V Trace Ramsink driver"
+	depends on RVTRACE
+	default y
+	help
+	  This driver provides support for Risc-V E-Trace Ramsink
+	  component.
drivers/hwtracing/rvtrace/Makefile +1 -0
index f320693a1fc5..122e575da9fb 100644
@@ -3,3 +3,4 @@
  obj-$(CONFIG_RVTRACE) += rvtrace.o
  rvtrace-y := rvtrace-core.o rvtrace-platform.o
  obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
+obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +104 -0
new file mode 100644
index 000000000000..7bd0cf1e4dfd
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/dma-mapping.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+
+#define RVTRACE_RAMSINK_STARTLOW_OFF		0x010
+#define RVTRACE_RAMSINK_STARTHIGH_OFF		0x014
+#define RVTRACE_RAMSINK_LIMITLOW_OFF		0x018
+#define RVTRACE_RAMSINK_LIMITHIGH_OFF		0x01c
+#define RVTRACE_RAMSINK_WPLOW_OFF		0x020
+#define RVTRACE_RAMSINK_WPHIGH_OFF		0x024
+#define RVTRACE_RAMSINK_RPLOW_OFF		0x028
+#define RVTRACE_RAMSINK_RPHIGH_OFF		0x02c
+
+struct rvtrace_ramsink_priv {
+	size_t size;
+	void *va;
+	dma_addr_t start;
+	dma_addr_t end;
+	/* WP from prev iteration */
+	dma_addr_t prev_head;
+};
+
+struct trace_buf {
+	void *base;
+	size_t size;
+	int cur, len;
+};
+
+static void tbuf_to_pbuf_copy(struct trace_buf *src, struct trace_buf *dst)
+{
+	int bytes_dst, bytes_src, bytes;
+	void *dst_addr, *src_addr;
+
+	while (src->size) {
+		src_addr = src->base + src->cur;
+		dst_addr = dst->base + dst->cur;
+
+		if (dst->len - dst->cur < src->size)
+			bytes_dst = dst->len - dst->cur;
+		else
+			bytes_dst = src->size;
+		if (src->len - src->cur < src->size)
+			bytes_src = src->len - src->cur;
+		else
+			bytes_src = src->size;
+		bytes = bytes_dst < bytes_src ? bytes_dst : bytes_src;
+		memcpy(dst_addr, src_addr, bytes);
+		dst->cur = (dst->cur + bytes) % dst->len;
+		src->cur = (src->cur + bytes) % src->len;
+		src->size -= bytes;
+	}
+}
+
+static size_t rvtrace_ramsink_copyto_auxbuf(struct rvtrace_component *comp,
+					    struct rvtrace_perf_auxbuf *buf)
+{
+	struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+	struct trace_buf src, dst;
+	u32 wp_low, wp_high;
+	u64 buf_cur_head;
+	size_t size;
+
+	wp_low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPLOW_OFF);
+	wp_high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPHIGH_OFF);
+	buf_cur_head = (u64)(wp_high) << 32 | wp_low;
+
+	if (buf_cur_head == priv->prev_head)
+		return 0;
+
+	dst.base = buf->base;
+	dst.len = buf->length;
+	dst.cur = buf->pos;
+	dst.size = 0;
+
+	src.base = priv->va;
+	src.len = priv->end - priv->start;
+	if (buf_cur_head > priv->prev_head) {
+		src.size = buf_cur_head - priv->prev_head;
+	} else {
+		src.size = priv->end - priv->prev_head;
+		src.size += buf_cur_head - priv->start;
+	}
+
+	src.cur = buf_cur_head - priv->start;
+	size = src.size;
+	tbuf_to_pbuf_copy(&src, &dst);
+	buf->pos = dst.cur;
+	priv->prev_head = buf_cur_head;
+
+	return size;
+}
+

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 114

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +10 -0
+static int rvtrace_ramsink_setup(struct rvtrace_component *comp)
+{
+	struct rvtrace_ramsink_priv *priv;
+
+	priv = devm_kzalloc(&comp->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev_set_drvdata(&comp->dev, priv);
+
+	priv->size = SZ_4M;

Can we make this size dynamically determined? 4M seems inadequate. This is exceedingly so if the RAM sink is linked to a funnel, where you can have many harts dumping traces into this sink.

drivers/hwtracing/rvtrace/rvtrace-ramsink.c

Quoted 12 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +12 -0
+	priv->va = dma_alloc_coherent(&comp->dev, priv->size, &priv->start, GFP_KERNEL);
+	if (!priv->va)
+		return -ENOMEM;
+	priv->end = priv->start + priv->size;
+	priv->prev_head = priv->start;
+
+	/* Setup ram sink addresses */
+	rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_STARTLOW_OFF);
+	rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_STARTHIGH_OFF);
+	rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_WPLOW_OFF);
+	rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_WPHIGH_OFF);
+	/* Limit address needs to be set to end - 4 so that HW doesn't cause an overflow. */> +	rvtrace_write32(comp->pdata, lower_32_bits(priv->end - 0x4), RVTRACE_RAMSINK_LIMITLOW_OFF);

Should not hardcode 4 as the trace write width. Control Interface Spec chapter 7.1 has the proper instruction on how to probe for this width:

"Not every value may be settable in trRamStart/Limit registers. Value
written may be trimmed (for example aligned on a particular 2^N boundary) and a trace tool should verify values being written"

Quoted 61 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +61 -0
+	rvtrace_write32(comp->pdata, upper_32_bits(priv->end), RVTRACE_RAMSINK_LIMITHIGH_OFF);
+
+	return 0;
+}
+
+static void rvtrace_ramsink_cleanup(struct rvtrace_component *comp)
+{
+	struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+
+	dma_free_coherent(&comp->dev, priv->size, priv->va, priv->start);
+}
+
+static int rvtrace_ramsink_probe(struct rvtrace_component *comp)
+{
+	int ret;
+
+	ret = rvtrace_ramsink_setup(comp);
+	if (ret)
+		return dev_err_probe(&comp->dev, ret, "failed to setup ramsink.\n");
+
+	ret = rvtrace_enable_component(comp);
+	if (ret)
+		return dev_err_probe(&comp->dev, ret, "failed to enable ramsink.\n");
+
+	return ret;
+}
+
+static void rvtrace_ramsink_remove(struct rvtrace_component *comp)
+{
+	int ret;
+
+	ret = rvtrace_disable_component(comp);
+	if (ret)
+		dev_err(&comp->dev, "failed to disable ramsink.\n");
+
+	rvtrace_ramsink_cleanup(comp);
+}
+
+static struct rvtrace_component_id rvtrace_ramsink_ids[] = {
+	{ .type = RVTRACE_COMPONENT_TYPE_RAMSINK,
+	  .version = rvtrace_component_mkversion(1, 0), },
+	{},
+};
+
+static struct rvtrace_driver rvtrace_ramsink_driver = {
+	.id_table = rvtrace_ramsink_ids,
+	.copyto_auxbuf = rvtrace_ramsink_copyto_auxbuf,
+	.probe = rvtrace_ramsink_probe,
+	.remove = rvtrace_ramsink_remove,
+	.driver = {
+		.name = "rvtrace-ramsink",
+	},
+};
+
+static int __init rvtrace_ramsink_init(void)
+{
+	return rvtrace_register_driver(&rvtrace_ramsink_driver);
+}
+
+static void __exit rvtrace_ramsink_exit(void)
+{

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 198

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +10 -0
+	rvtrace_unregister_driver(&rvtrace_ramsink_driver);
+}
+
+module_init(rvtrace_ramsink_init);
+module_exit(rvtrace_ramsink_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mayuresh Chitale <[email protected]>");
+MODULE_DESCRIPTION("RISC-V Trace Ramsink Driver");
+MODULE_LICENSE("GPL");

Bo

A message that is not in the archive (sent off-list or not yet received): CAN37VV5J2+gzpraR2NhaJBNfQ3dPsr-72Mmg03+ykcLoouZ8_Q@mail.gmail.com

#27 lore raw

Re: [PATCH 07/11] rvtrace: Add trace ramsink driver

Quoted 181 lines · Mayuresh Chitale
Quoted 178 lines · On Tue, Oct 7, 2025 at 1:21 PM Bo Gan <[email protected]…
Quoted 150 lines · Anup Patel

From: Mayuresh Chitale <[email protected] <mailto:[email protected]>>

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

Co-developed-by Anup Patel <[email protected] <mailto:[email protected]>>
Signed-off-by Anup Patel <[email protected] <mailto:[email protected]>>
Signed-off-by Mayuresh Chitale <[email protected] <mailto:[email protected]>>

---
  drivers/hwtracing/rvtrace/Kconfig           |   8 + drivers/hwtracing/rvtrace/Makefile          |   1 + drivers/hwtracing/rvtrace/rvtrace-ramsink.c | 198 ++++++++++++++++++++

  3 files changed, 207 insertions(+)

  create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c

drivers/hwtracing/rvtrace/Kconfig +0 -0
index ba35c05f3f54..aef7e9989165 100644
@@ -21,3 +21,11 @@ config RVTRACE_ENCODER

      default y
      help
        This driver provides support for RISC-V Trace Encoder component.

drivers/hwtracing/rvtrace/Kconfig +8 -0
+
+config RVTRACE_RAMSINK
+     tristate "RISC-V Trace Ramsink driver"
+     depends on RVTRACE
+     default y
+     help
+       This driver provides support for Risc-V E-Trace Ramsink
+       component.
drivers/hwtracing/rvtrace/Makefile +0 -0
index f320693a1fc5..122e575da9fb 100644
@@ -3,3 +3,4 @@

  obj-$(CONFIG_RVTRACE) += rvtrace.o rvtrace-y := rvtrace-core.o rvtrace-platform.o obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o

drivers/hwtracing/rvtrace/Makefile +1 -0
+obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +104 -0
new file mode 100644
index 000000000000..7bd0cf1e4dfd
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/dma-mapping.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+
+#define RVTRACE_RAMSINK_STARTLOW_OFF         0x010
+#define RVTRACE_RAMSINK_STARTHIGH_OFF                0x014
+#define RVTRACE_RAMSINK_LIMITLOW_OFF         0x018
+#define RVTRACE_RAMSINK_LIMITHIGH_OFF                0x01c
+#define RVTRACE_RAMSINK_WPLOW_OFF            0x020
+#define RVTRACE_RAMSINK_WPHIGH_OFF           0x024
+#define RVTRACE_RAMSINK_RPLOW_OFF            0x028
+#define RVTRACE_RAMSINK_RPHIGH_OFF           0x02c
+
+struct rvtrace_ramsink_priv {
+     size_t size;
+     void *va;
+     dma_addr_t start;
+     dma_addr_t end;
+     /* WP from prev iteration */
+     dma_addr_t prev_head;
+};
+
+struct trace_buf {
+     void *base;
+     size_t size;
+     int cur, len;
+};
+
+static void tbuf_to_pbuf_copy(struct trace_buf *src, struct trace_buf *dst)
+{
+     int bytes_dst, bytes_src, bytes;
+     void *dst_addr, *src_addr;
+
+     while (src->size) {
+             src_addr = src->base + src->cur;
+             dst_addr = dst->base + dst->cur;
+
+             if (dst->len - dst->cur < src->size)
+                     bytes_dst = dst->len - dst->cur;
+             else
+                     bytes_dst = src->size;
+             if (src->len - src->cur < src->size)
+                     bytes_src = src->len - src->cur;
+             else
+                     bytes_src = src->size;
+             bytes = bytes_dst < bytes_src ? bytes_dst : bytes_src;
+             memcpy(dst_addr, src_addr, bytes);
+             dst->cur = (dst->cur + bytes) % dst->len;
+             src->cur = (src->cur + bytes) % src->len;
+             src->size -= bytes;
+     }
+}
+
+static size_t rvtrace_ramsink_copyto_auxbuf(struct rvtrace_component *comp,
+                                         struct rvtrace_perf_auxbuf *buf)
+{
+     struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+     struct trace_buf src, dst;
+     u32 wp_low, wp_high;
+     u64 buf_cur_head;
+     size_t size;
+
+     wp_low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPLOW_OFF);
+     wp_high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPHIGH_OFF);
+     buf_cur_head = (u64)(wp_high) << 32 | wp_low;
+
+     if (buf_cur_head == priv->prev_head)
+             return 0;
+
+     dst.base = buf->base;
+     dst.len = buf->length;
+     dst.cur = buf->pos;
+     dst.size = 0;
+
+     src.base = priv->va;
+     src.len = priv->end - priv->start;
+     if (buf_cur_head > priv->prev_head) {
+             src.size = buf_cur_head - priv->prev_head;
+     } else {
+             src.size = priv->end - priv->prev_head;
+             src.size += buf_cur_head - priv->start;
+     }
+
+     src.cur = buf_cur_head - priv->start;
+     size = src.size;
+     tbuf_to_pbuf_copy(&src, &dst);
+     buf->pos = dst.cur;
+     priv->prev_head = buf_cur_head;
+
+     return size;
+}
+

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 114

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +10 -0
+static int rvtrace_ramsink_setup(struct rvtrace_component *comp)
+{
+     struct rvtrace_ramsink_priv *priv;
+
+     priv = devm_kzalloc(&comp->dev, sizeof(*priv), GFP_KERNEL);
+     if (!priv)
+             return -ENOMEM;
+     dev_set_drvdata(&comp->dev, priv);
+
+     priv->size = SZ_4M;

Can we make this size dynamically determined? 4M seems inadequate. This is exceedingly so if the RAM sink is linked to a funnel, where you can have many harts dumping traces into this sink.

Quoted 12 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +12 -0
+     priv->va = dma_alloc_coherent(&comp->dev, priv->size, &priv->start, GFP_KERNEL);
+     if (!priv->va)
+             return -ENOMEM;
+     priv->end = priv->start + priv->size;
+     priv->prev_head = priv->start;
+
+     /* Setup ram sink addresses */
+     rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_STARTLOW_OFF);
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_STARTHIGH_OFF);
+     rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_WPLOW_OFF);
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_WPHIGH_OFF);
+     /* Limit address needs to be set to end - 4 so that HW doesn't cause an overflow. */> + rvtrace_write32(comp->pdata, lower_32_bits(priv->end - 0x4), RVTRACE_RAMSINK_LIMITLOW_OFF);
Quoted 8 lines
Quoted 7 lines

Should not hardcode 4 as the trace write width. Control Interface Spec chapter 7.1 has the proper instruction on how to probe for this width:

"Not every value may be settable in trRamStart/Limit registers. Value written may be trimmed (for example aligned on a particular 2^N boundary) and a trace tool should verify values being written"

Thanks for the comments. I will incorporate those in v2.

@Mayuresh, I went over the spec again. I don't see the handling off wrap. You need to detect if the buffer has wrapped around by testing bit 0 of trRamWPLow. So:

a. trRamWPLow needs masking bit 0 before being used as address to read
trace data.
b. If wrapped, then the trace buffer is actually scattered in two parts:
[WP, Limit + width) and [Start, WP). During copying, you need to use the right portion of the trace buffer.

ref: https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L260

BTW, again your mail wasn't delivered to the mailing-list for some reason Please check: I couldn't find your name on

https://lists.infradead.org/pipermail/linux-riscv/2025-October/thread.html
https://lore.kernel.org/all/[email protected]/

Quoted 66 lines
Quoted 66 lines
Quoted 66 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +65 -0
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->end), RVTRACE_RAMSINK_LIMITHIGH_OFF);
+
+     return 0;
+}
+
+static void rvtrace_ramsink_cleanup(struct rvtrace_component *comp)
+{
+     struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+
+     dma_free_coherent(&comp->dev, priv->size, priv->va, priv->start);
+}
+
+static int rvtrace_ramsink_probe(struct rvtrace_component *comp)
+{
+     int ret;
+
+     ret = rvtrace_ramsink_setup(comp);
+     if (ret)
+             return dev_err_probe(&comp->dev, ret, "failed to setup ramsink.\n");
+
+     ret = rvtrace_enable_component(comp);
+     if (ret)
+             return dev_err_probe(&comp->dev, ret, "failed to enable ramsink.\n");
+
+     return ret;
+}
+
+static void rvtrace_ramsink_remove(struct rvtrace_component *comp)
+{
+     int ret;
+
+     ret = rvtrace_disable_component(comp);
+     if (ret)
+             dev_err(&comp->dev, "failed to disable ramsink.\n");
+
+     rvtrace_ramsink_cleanup(comp);
+}
+
+static struct rvtrace_component_id rvtrace_ramsink_ids[] = {
+     { .type = RVTRACE_COMPONENT_TYPE_RAMSINK,
+       .version = rvtrace_component_mkversion(1, 0), },
+     {},
+};
+
+static struct rvtrace_driver rvtrace_ramsink_driver = {
+     .id_table = rvtrace_ramsink_ids,
+     .copyto_auxbuf = rvtrace_ramsink_copyto_auxbuf,
+     .probe = rvtrace_ramsink_probe,
+     .remove = rvtrace_ramsink_remove,
+     .driver = {
+             .name = "rvtrace-ramsink",
+     },
+};
+
+static int __init rvtrace_ramsink_init(void)
+{
+     return rvtrace_register_driver(&rvtrace_ramsink_driver);
+}
+
+static void __exit rvtrace_ramsink_exit(void)
+{
+     rvtrace_unregister_driver(&rvtrace_ramsink_driver);
+}
+
+module_init(rvtrace_ramsink_init);
Quoted 8 lines
Quoted 8 lines

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 198

Quoted 6 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +6 -0
+module_exit(rvtrace_ramsink_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mayuresh Chitale <[email protected] <mailto:[email protected]>>");
+MODULE_DESCRIPTION("RISC-V Trace Ramsink Driver");
+MODULE_LICENSE("GPL");

Bo

Bo

#28 lore raw

Re: [PATCH 07/11] rvtrace: Add trace ramsink driver

Hi Bo,

Quoted 190 lines · Bo Gan
Quoted 188 lines · Mayuresh Chitale
Quoted 185 lines · On Tue, Oct 7, 2025 at 1:21 PM Bo Gan <[email protected]…
Quoted 150 lines · Anup Patel

From: Mayuresh Chitale <[email protected] <mailto:[email protected]>>

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

Co-developed-by Anup Patel <[email protected] <mailto:[email protected]>>
Signed-off-by Anup Patel <[email protected] <mailto:[email protected]>>
Signed-off-by Mayuresh Chitale <[email protected] <mailto:[email protected]>>
---
  drivers/hwtracing/rvtrace/Kconfig           |   8 +
  drivers/hwtracing/rvtrace/Makefile          |   1 +
  drivers/hwtracing/rvtrace/rvtrace-ramsink.c | 198 ++++++++++++++++++++
  3 files changed, 207 insertions(+)

create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c

drivers/hwtracing/rvtrace/Kconfig +8 -0
index ba35c05f3f54..aef7e9989165 100644
@@ -21,3 +21,11 @@ config RVTRACE_ENCODER
      default y
      help
        This driver provides support for RISC-V Trace Encoder component.
+
+config RVTRACE_RAMSINK
+     tristate "RISC-V Trace Ramsink driver"
+     depends on RVTRACE
+     default y
+     help
+       This driver provides support for Risc-V E-Trace Ramsink
+       component.
drivers/hwtracing/rvtrace/Makefile +1 -0
index f320693a1fc5..122e575da9fb 100644
@@ -3,3 +3,4 @@
  obj-$(CONFIG_RVTRACE) += rvtrace.o
  rvtrace-y := rvtrace-core.o rvtrace-platform.o
  obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
+obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +104 -0
new file mode 100644
index 000000000000..7bd0cf1e4dfd
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Ventana Micro Systems Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/dma-mapping.h>
+#include <linux/rvtrace.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+
+#define RVTRACE_RAMSINK_STARTLOW_OFF         0x010
+#define RVTRACE_RAMSINK_STARTHIGH_OFF                0x014
+#define RVTRACE_RAMSINK_LIMITLOW_OFF         0x018
+#define RVTRACE_RAMSINK_LIMITHIGH_OFF                0x01c
+#define RVTRACE_RAMSINK_WPLOW_OFF            0x020
+#define RVTRACE_RAMSINK_WPHIGH_OFF           0x024
+#define RVTRACE_RAMSINK_RPLOW_OFF            0x028
+#define RVTRACE_RAMSINK_RPHIGH_OFF           0x02c
+
+struct rvtrace_ramsink_priv {
+     size_t size;
+     void *va;
+     dma_addr_t start;
+     dma_addr_t end;
+     /* WP from prev iteration */
+     dma_addr_t prev_head;
+};
+
+struct trace_buf {
+     void *base;
+     size_t size;
+     int cur, len;
+};
+
+static void tbuf_to_pbuf_copy(struct trace_buf *src, struct trace_buf *dst)
+{
+     int bytes_dst, bytes_src, bytes;
+     void *dst_addr, *src_addr;
+
+     while (src->size) {
+             src_addr = src->base + src->cur;
+             dst_addr = dst->base + dst->cur;
+
+             if (dst->len - dst->cur < src->size)
+                     bytes_dst = dst->len - dst->cur;
+             else
+                     bytes_dst = src->size;
+             if (src->len - src->cur < src->size)
+                     bytes_src = src->len - src->cur;
+             else
+                     bytes_src = src->size;
+             bytes = bytes_dst < bytes_src ? bytes_dst : bytes_src;
+             memcpy(dst_addr, src_addr, bytes);
+             dst->cur = (dst->cur + bytes) % dst->len;
+             src->cur = (src->cur + bytes) % src->len;
+             src->size -= bytes;
+     }
+}
+
+static size_t rvtrace_ramsink_copyto_auxbuf(struct rvtrace_component *comp,
+                                         struct rvtrace_perf_auxbuf *buf)
+{
+     struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+     struct trace_buf src, dst;
+     u32 wp_low, wp_high;
+     u64 buf_cur_head;
+     size_t size;
+
+     wp_low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPLOW_OFF);
+     wp_high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPHIGH_OFF);
+     buf_cur_head = (u64)(wp_high) << 32 | wp_low;
+
+     if (buf_cur_head == priv->prev_head)
+             return 0;
+
+     dst.base = buf->base;
+     dst.len = buf->length;
+     dst.cur = buf->pos;
+     dst.size = 0;
+
+     src.base = priv->va;
+     src.len = priv->end - priv->start;
+     if (buf_cur_head > priv->prev_head) {
+             src.size = buf_cur_head - priv->prev_head;
+     } else {
+             src.size = priv->end - priv->prev_head;
+             src.size += buf_cur_head - priv->start;
+     }
+
+     src.cur = buf_cur_head - priv->start;
+     size = src.size;
+     tbuf_to_pbuf_copy(&src, &dst);
+     buf->pos = dst.cur;
+     priv->prev_head = buf_cur_head;
+
+     return size;
+}
+

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 114

Quoted 10 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +10 -0
+static int rvtrace_ramsink_setup(struct rvtrace_component *comp)
+{
+     struct rvtrace_ramsink_priv *priv;
+
+     priv = devm_kzalloc(&comp->dev, sizeof(*priv), GFP_KERNEL);
+     if (!priv)
+             return -ENOMEM;
+     dev_set_drvdata(&comp->dev, priv);
+
+     priv->size = SZ_4M;

Can we make this size dynamically determined? 4M seems inadequate. This is exceedingly so if the RAM sink is linked to a funnel, where you can have many harts dumping traces into this sink.

drivers/hwtracing/rvtrace/rvtrace-ramsink.c

Quoted 12 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +12 -0
+     priv->va = dma_alloc_coherent(&comp->dev, priv->size, &priv->start, GFP_KERNEL);
+     if (!priv->va)
+             return -ENOMEM;
+     priv->end = priv->start + priv->size;
+     priv->prev_head = priv->start;
+
+     /* Setup ram sink addresses */
+     rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_STARTLOW_OFF);
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_STARTHIGH_OFF);
+     rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_WPLOW_OFF);
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_WPHIGH_OFF);
+     /* Limit address needs to be set to end - 4 so that HW doesn't cause an overflow. */> + rvtrace_write32(comp->pdata, lower_32_bits(priv->end - 0x4), RVTRACE_RAMSINK_LIMITLOW_OFF);

Should not hardcode 4 as the trace write width. Control Interface Spec chapter 7.1 has the proper instruction on how to probe for this width:

"Not every value may be settable in trRamStart/Limit registers. Value
written may be trimmed (for example aligned on a particular 2^N boundary) and a trace tool should verify values being written"

Quoted 8 lines
Quoted 1 line

Thanks for the comments. I will incorporate those in v2.

@Mayuresh, I went over the spec again. I don't see the handling off wrap. You need to detect if the buffer has wrapped around by testing bit 0 of trRamWPLow. So:

a. trRamWPLow needs masking bit 0 before being used as address to read
trace data.

The trRamWPLow isn't used directly and the size is calculated based on current head(wpLow) and previous head values but I agree that trRamWrap needs to be masked.

Quoted 3 lines

b. If wrapped, then the trace buffer is actually scattered in two parts:
[WP, Limit + width) and [Start, WP). During copying, you need to use the right portion of the trace buffer.

This is done using the buf_cur_head and priv->head variables.

Quoted 8 lines

ref: https://github.com/ganboing/riscv-trace-umd/blob/master/rvtrace/funnel.py#L260

BTW, again your mail wasn't delivered to the mailing-list for some reason Please check: I couldn't find your name on

https://lists.infradead.org/pipermail/linux-riscv/2025-October/thread.html
https://lore.kernel.org/all/[email protected]/

In a previous reply the mode wasn't set to 'plain text'. I hope this email gets delivered.

Quoted 77 lines
Quoted 67 lines
Quoted 67 lines
Quoted 67 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +65 -0
+     rvtrace_write32(comp->pdata, upper_32_bits(priv->end), RVTRACE_RAMSINK_LIMITHIGH_OFF);
+
+     return 0;
+}
+
+static void rvtrace_ramsink_cleanup(struct rvtrace_component *comp)
+{
+     struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
+
+     dma_free_coherent(&comp->dev, priv->size, priv->va, priv->start);
+}
+
+static int rvtrace_ramsink_probe(struct rvtrace_component *comp)
+{
+     int ret;
+
+     ret = rvtrace_ramsink_setup(comp);
+     if (ret)
+             return dev_err_probe(&comp->dev, ret, "failed to setup ramsink.\n");
+
+     ret = rvtrace_enable_component(comp);
+     if (ret)
+             return dev_err_probe(&comp->dev, ret, "failed to enable ramsink.\n");
+
+     return ret;
+}
+
+static void rvtrace_ramsink_remove(struct rvtrace_component *comp)
+{
+     int ret;
+
+     ret = rvtrace_disable_component(comp);
+     if (ret)
+             dev_err(&comp->dev, "failed to disable ramsink.\n");
+
+     rvtrace_ramsink_cleanup(comp);
+}
+
+static struct rvtrace_component_id rvtrace_ramsink_ids[] = {
+     { .type = RVTRACE_COMPONENT_TYPE_RAMSINK,
+       .version = rvtrace_component_mkversion(1, 0), },
+     {},
+};
+
+static struct rvtrace_driver rvtrace_ramsink_driver = {
+     .id_table = rvtrace_ramsink_ids,
+     .copyto_auxbuf = rvtrace_ramsink_copyto_auxbuf,
+     .probe = rvtrace_ramsink_probe,
+     .remove = rvtrace_ramsink_remove,
+     .driver = {
+             .name = "rvtrace-ramsink",
+     },
+};
+
+static int __init rvtrace_ramsink_init(void)
+{
+     return rvtrace_register_driver(&rvtrace_ramsink_driver);
+}
+
+static void __exit rvtrace_ramsink_exit(void)
+{
+     rvtrace_unregister_driver(&rvtrace_ramsink_driver);
+}
+
+module_init(rvtrace_ramsink_init);
Quoted 8 lines
Quoted 8 lines

drivers/hwtracing/rvtrace/rvtrace-ramsink.c line 198

Quoted 6 lines
drivers/hwtracing/rvtrace/rvtrace-ramsink.c new file +6 -0
+module_exit(rvtrace_ramsink_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mayuresh Chitale <[email protected] <mailto:[email protected]>>");
+MODULE_DESCRIPTION("RISC-V Trace Ramsink Driver");
+MODULE_LICENSE("GPL");

Bo

Bo

author
#29 lore raw

PATCH 8/11 rvtrace: Add perf driver for tracing using perf tool

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

Add perf driver for RISC-V tracing similar to ARM Coresight and Hisilicon PTT drivers. The driver adds 'rvtrace' event descriptor which can be used by the perf tool to record the RISC-V trace data.

---
 drivers/hwtracing/rvtrace/Kconfig        |   1 +
 drivers/hwtracing/rvtrace/Makefile       |   2 +-
 drivers/hwtracing/rvtrace/rvtrace-core.c |   8 +
 drivers/hwtracing/rvtrace/rvtrace-perf.c | 343 +++++++++++++++++++++++
 include/linux/rvtrace.h                  |   3 +
 5 files changed, 356 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwtracing/rvtrace/rvtrace-perf.c
drivers/hwtracing/rvtrace/Kconfig +1 -0
index aef7e9989165..76379c63c5c3 100644
@@ -4,6 +4,7 @@ menuconfig RVTRACE
 	tristate "RISC-V Trace Support"
 	depends on RISCV
 	depends on OF
+	select PERF_EVENTS
 	default RISCV
 	help
 	  This framework provides a kernel interface for the RISC-V trace
drivers/hwtracing/rvtrace/Makefile +1 -1
index 122e575da9fb..07403f4d94e3 100644
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_RVTRACE) += rvtrace.o
-rvtrace-y := rvtrace-core.o rvtrace-platform.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o rvtrace-perf.o
 obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
 obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
drivers/hwtracing/rvtrace/rvtrace-core.c +8 -0
index e874899c8b43..5d4e92f22ccd 100644
@@ -760,11 +760,19 @@ static int __init rvtrace_init(void)
 		return ret;
 	}
 
+	ret = rvtrace_perf_init();
+	if (ret) {
+		platform_driver_unregister(&rvtrace_platform_driver);
+		bus_unregister(&rvtrace_bustype);
+		return ret;
+	}
+
 	return 0;
 }
 
 static void __exit rvtrace_exit(void)
 {
+	rvtrace_perf_exit();
 	platform_driver_unregister(&rvtrace_platform_driver);
 	bus_unregister(&rvtrace_bustype);
 }
drivers/hwtracing/rvtrace/rvtrace-perf.c new file +343 -0

343 lines not on this page · raw message

include/linux/rvtrace.h +3 -0
index 17d2fd9234c2..0a454707633b 100644
@@ -335,4 +335,7 @@ static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
 		driver_unregister(&rtdrv->driver);
 }
 
+int rvtrace_perf_init(void);
+void rvtrace_perf_exit(void);
+
 #endif
Signature · 1 line
2.43.0
author
#30 lore raw

PATCH 9/11 perf tools: Add RISC-V trace PMU record capabilities

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

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

---
 tools/perf/arch/riscv/util/Build      |   1 +
 tools/perf/arch/riscv/util/auxtrace.c | 218 ++++++++++++++++++++++++++
 tools/perf/util/auxtrace.c            |   1 +
 tools/perf/util/auxtrace.h            |   1 +
 tools/perf/util/rvtrace.h             |  18 +++
 5 files changed, 239 insertions(+)
 create mode 100644 tools/perf/arch/riscv/util/auxtrace.c
 create mode 100644 tools/perf/util/rvtrace.h
tools/perf/arch/riscv/util/Build +1 -0
index 58a672246024..2ba1fb98a0ad 100644
@@ -3,3 +3,4 @@ perf-util-y += header.o
 
 perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
 perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+perf-util-$(CONFIG_AUXTRACE) += auxtrace.o
tools/perf/arch/riscv/util/auxtrace.c new file +218 -0

218 lines not on this page · raw message

tools/perf/util/auxtrace.c +1 -0
index ebd32f1b8f12..735f6c360064 100644
@@ -1393,6 +1393,7 @@ int perf_event__process_auxtrace_info(struct perf_session *session,
 	case PERF_AUXTRACE_HISI_PTT:
 		err = hisi_ptt_process_auxtrace_info(event, session);
 		break;
+	case PERF_AUXTRACE_RISCV_TRACE:
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
tools/perf/util/auxtrace.h +1 -0
index f001cbb68f8e..5b7ce4a99709 100644
@@ -50,6 +50,7 @@ enum auxtrace_type {
 	PERF_AUXTRACE_ARM_SPE,
 	PERF_AUXTRACE_S390_CPUMSF,
 	PERF_AUXTRACE_HISI_PTT,
+	PERF_AUXTRACE_RISCV_TRACE,
 };
 
 enum itrace_period_type {
tools/perf/util/rvtrace.h new file +18 -0
new file mode 100644
index 000000000000..93c041db8660
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <[email protected]>
+ */
+
+#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>
+
+#define RVTRACE_AUXTRACE_PRIV_SIZE	sizeof(u64)
+
+#endif
Signature · 1 line
2.43.0
author
#31 lore raw

PATCH 10/11 perf tools: Initial support for RISC-V trace decoder

In reply to Anup Patel

From: Mayuresh Chitale <[email protected]>

Add bare bones support for RISC-V trace decoder so that the data received from the hardware by the RISC-V trace perf driver can be written to the perf record output file.

---
 tools/perf/util/Build             |  1 +
 tools/perf/util/auxtrace.c        |  3 +
 tools/perf/util/rvtrace-decoder.c | 91 +++++++++++++++++++++++++++++++
 tools/perf/util/rvtrace.h         |  2 +
 4 files changed, 97 insertions(+)
 create mode 100644 tools/perf/util/rvtrace-decoder.c
tools/perf/util/Build +1 -0
index 4959e7a990e4..2305638283c4 100644
@@ -142,6 +142,7 @@ perf-util-$(CONFIG_AUXTRACE) += cs-etm.o
 perf-util-$(CONFIG_AUXTRACE) += cs-etm-decoder/
 endif
 perf-util-$(CONFIG_AUXTRACE) += cs-etm-base.o
+perf-util-$(CONFIG_AUXTRACE) += rvtrace-decoder.o
 
 perf-util-y += parse-branch-options.o
 perf-util-y += dump-insn.o
tools/perf/util/auxtrace.c +3 -0
index 735f6c360064..5ccb5f3d9af8 100644
@@ -54,6 +54,7 @@
 #include "arm-spe.h"
 #include "hisi-ptt.h"
 #include "s390-cpumsf.h"
+#include "rvtrace.h"
 #include "util/mmap.h"
 
 #include <linux/ctype.h>
@@ -1394,6 +1395,8 @@ int perf_event__process_auxtrace_info(struct perf_session *session,
 		err = hisi_ptt_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;
tools/perf/util/rvtrace-decoder.c new file +91 -0

91 lines not on this page · raw message

tools/perf/util/rvtrace.h +2 -0
index 93c041db8660..fdf2e5866c85 100644
@@ -15,4 +15,6 @@
 
 #define RVTRACE_AUXTRACE_PRIV_SIZE	sizeof(u64)
 
+int rvtrace__process_auxtrace_info(union perf_event *event, struct perf_session *session);
+struct auxtrace_record *rvtrace_record_init(int *err);
 #endif
Signature · 1 line
2.43.0
author
#32 lore raw

PATCH 11/11 MAINTAINERS: Add entry for RISC-V trace framework and drivers

In reply to Anup Patel

Add Mayuresh and myself as maintainers for RISC-V trace framework and drivers.

---
 MAINTAINERS | 11 +++++++++++
 1 file changed, 11 insertions(+)
MAINTAINERS +11 -0
index 969df0192fc1..898b51d3dd8a 100644
@@ -21800,6 +21800,17 @@ F:	include/dt-bindings/power/thead,th1520-power.h
 F:	include/dt-bindings/reset/thead,th1520-reset.h
 F:	include/linux/firmware/thead/thead,th1520-aon.h
 
+RISC-V TRACE FRAMEWORK AND DRIVERS
+M:	Mayuresh Chitale <[email protected]>
+M:	Anup Patel <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/riscv/riscv,trace-component.yaml
+F:	drivers/hwtracing/rvtrace/*
+F:	include/linux/rvtrace*
+F:	tools/perf/arch/riscv/util/auxtrace.c
+F:	tools/perf/util/rvtrace*
+
 RNBD BLOCK DRIVERS
 M:	Md. Haris Iqbal <[email protected]>
 M:	Jack Wang <[email protected]>
Signature · 1 line
2.43.0
#33 lore raw
In reply to Anup Patel
Quoted 5 lines · Anup Patel

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

Quoted 7 lines

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

In ther future, there will be subsequent series adding:
1) Sysfs support

why does "trace" need sysfs support? No other cpu platform uses that today, so why is a new user/kernel api needed?

thanks,

greg k-h

author
#34 lore raw
Quoted 12 lines · Greg KH
Quoted 10 lines · Anup Patel

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

Quoted 5 lines
Quoted 2 lines

In ther future, there will be subsequent series adding:
1) Sysfs support

why does "trace" need sysfs support? No other cpu platform uses that today, so why is a new user/kernel api needed?

We saw trace support for other architectures (e.g. ARM coresight) allowing trace start/stop through sysfs. If this is an obsolete or not preferred approach then we will deprioritize and possibly never add it.

Regards,
Anup

#35 lore raw
Quoted 16 lines · Anup Patel
Quoted 15 lines · Greg KH
Quoted 12 lines · Anup Patel

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

In ther future, there will be subsequent series adding:
1) Sysfs support

Quoted 6 lines
Quoted 2 lines

why does "trace" need sysfs support? No other cpu platform uses that today, so why is a new user/kernel api needed?

We saw trace support for other architectures (e.g. ARM coresight) allowing trace start/stop through sysfs. If this is an obsolete or not preferred approach then we will deprioritize and possibly never add it.

Why is that needed for coresight and other arches do not need it? Perhaps it should be deleted from that codebase instead?

thanks,

greg k-h

#36 lore raw
Quoted 24 lines · Greg KH
Quoted 16 lines · Anup Patel
Quoted 15 lines · Greg KH
Quoted 12 lines · Anup Patel

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

In ther future, there will be subsequent series adding:
1) Sysfs support

Quoted 6 lines
Quoted 2 lines

why does "trace" need sysfs support? No other cpu platform uses that today, so why is a new user/kernel api needed?

We saw trace support for other architectures (e.g. ARM coresight) allowing trace start/stop through sysfs. If this is an obsolete or not preferred approach then we will deprioritize and possibly never add it.

Quoted 6 lines

Why is that needed for coresight and other arches do not need it? Perhaps it should be deleted from that codebase instead?

thanks,

greg k-h

Hi Greg,

sysfs is helpful for controlling the trace if not utilized through perf framework. It can also be used by userspace to discover the topology of trace components and their capabilities. @Anup I assume this driver is designed with other sinks in mind (not just ramsink), so it can be used to emit trace to external probes, right?

Bo

author
#37 lore raw
Quoted 33 lines · Bo Gan
Quoted 24 lines · Greg KH
Quoted 16 lines · Anup Patel
Quoted 15 lines · Greg KH
Quoted 12 lines · Anup Patel

This series adds initial support for RISC-V trace framework and drivers. The RISC-V trace v1.0 specification is already ratified and can be found at:
https://github.com/riscv-non-isa/e-trace-encap/releases/tag/v1.0.0-ratified
https://github.com/riscv-non-isa/tg-nexus-trace/releases/tag/1.0_Ratified

The RISC-V trace framework and drivers are designed to be agnostic to the underlying trace protocol hence both RISC-V E-trace and RISC-V N-trace should work fine. The discovery of trace protocl parameters are left to user-space trace decoder.

In ther future, there will be subsequent series adding:
1) Sysfs support

Quoted 6 lines
Quoted 2 lines

why does "trace" need sysfs support? No other cpu platform uses that today, so why is a new user/kernel api needed?

We saw trace support for other architectures (e.g. ARM coresight) allowing trace start/stop through sysfs. If this is an obsolete or not preferred approach then we will deprioritize and possibly never add it.

Quoted 6 lines

Why is that needed for coresight and other arches do not need it? Perhaps it should be deleted from that codebase instead?

thanks,

greg k-h

Quoted 8 lines

Hi Greg,

sysfs is helpful for controlling the trace if not utilized through perf framework. It can also be used by userspace to discover the topology of trace components and their capabilities. @Anup I assume this driver is designed with other sinks in mind (not just ramsink), so it can be used to emit trace to external probes, right?

The rvtrace driver framework is intended to support all types of trace components and any topology between these components. The current patchset only adds encoder and ramsink trace component drivers since that is what we emulate in QEMU right now.

Regarding sysfs based tracing, the main use-case (like you mentioned) is the enabling tracing when the sinks are external devices which capture and store trace data somewhere outside. The perf based tracing is used for hosted tracing where the ramsink stores trace data in ram and perf tool captures it in the form of perf data.

Regards,
Anup