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