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
next prev 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