public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
From: Zane Leung <liangzhen@linux.spacemit.com>
To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org,
	alexander.shishkin@linux.intel.com, irogers@google.com
Cc: coresight@lists.linaro.org, peterz@infradead.org,
	mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com,
	jolsa@kernel.org, adrian.hunter@intel.com,
	kan.liang@linux.intel.com, mchitale@gmail.com,
	anup@brainfault.org, atish.patra@linux.dev,
	andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
	linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org,
	anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com,
	zhuangqiubin@linux.spacemit.com
Subject: [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver
Date: Tue, 14 Apr 2026 11:41:44 +0800	[thread overview]
Message-ID: <20260414034153.3272485-4-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com>

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

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

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

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


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

  parent reply	other threads:[~2026-04-14  3:43 UTC|newest]

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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260414034153.3272485-4-liangzhen@linux.spacemit.com \
    --to=liangzhen@linux.spacemit.com \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=andrew.jones@oss.qualcomm.com \
    --cc=anup.patel@oss.qualcomm.com \
    --cc=anup@brainfault.org \
    --cc=atish.patra@linux.dev \
    --cc=conor+dt@kernel.org \
    --cc=coresight@lists.linaro.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=irogers@google.com \
    --cc=jolsa@kernel.org \
    --cc=kan.liang@linux.intel.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=mark.rutland@arm.com \
    --cc=mayuresh.chitale@oss.qualcomm.com \
    --cc=mchitale@gmail.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=palmer@dabbelt.com \
    --cc=peterz@infradead.org \
    --cc=pjw@kernel.org \
    --cc=robh@kernel.org \
    --cc=sunilvl@oss.qualcomm.com \
    --cc=zhuangqiubin@linux.spacemit.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox