Add initial implementation of RISC-V Trace funnel driver. The funnel is defined in the RISC-V Trace Control Interface specification.
Signed-off-by: Zane Leung liangzhen@linux.spacemit.com --- drivers/hwtracing/gtrace/Kconfig | 14 ++ drivers/hwtracing/gtrace/Makefile | 1 + drivers/hwtracing/gtrace/rvtrace-funnel.c | 165 ++++++++++++++++++++ drivers/hwtracing/gtrace/rvtrace-funnel.h | 21 +++ drivers/hwtracing/gtrace/rvtrace-platform.c | 3 + drivers/hwtracing/gtrace/rvtrace.h | 1 + 6 files changed, 205 insertions(+) create mode 100644 drivers/hwtracing/gtrace/rvtrace-funnel.c create mode 100644 drivers/hwtracing/gtrace/rvtrace-funnel.h
diff --git a/drivers/hwtracing/gtrace/Kconfig b/drivers/hwtracing/gtrace/Kconfig index e5b62d65e2fd..f6026965c54d 100644 --- a/drivers/hwtracing/gtrace/Kconfig +++ b/drivers/hwtracing/gtrace/Kconfig @@ -57,3 +57,17 @@ config RVTRACE_RAMSINK
To compile this driver as a module, choose M here: the module will be called rvtrace-ramsink. + +config RVTRACE_FUNNEL + tristate "RISC-V Trace Funnel driver" + depends on RVTRACE + default y + help + This driver provides support for the RISC-V trace funnel component + which is defined by the RISC-V Trace Control Interface specification. + The funnel merges trace data from multiple input ports into a single + output stream. It is a link component in the trace path between + sources and sinks. + + To compile this driver as a module, choose M here: the module + will be called rvtrace-funnel. diff --git a/drivers/hwtracing/gtrace/Makefile b/drivers/hwtracing/gtrace/Makefile index 87cb5add78d4..9c99d917b9f4 100644 --- a/drivers/hwtracing/gtrace/Makefile +++ b/drivers/hwtracing/gtrace/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_RVTRACE) += rvtrace-platform.o obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o rvtrace-encoder-y := rvtrace-encoder-core.o rvtrace-encoder-sysfs.o obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o +obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o diff --git a/drivers/hwtracing/gtrace/rvtrace-funnel.c b/drivers/hwtracing/gtrace/rvtrace-funnel.c new file mode 100644 index 000000000000..941cd3360395 --- /dev/null +++ b/drivers/hwtracing/gtrace/rvtrace-funnel.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Spacemit Technologies, Inc. + */ + +#include <linux/device.h> +#include <linux/gtrace.h> +#include <linux/types.h> +#include "rvtrace.h" +#include "rvtrace-funnel.h" + +static int rvtrace_funnel_start(struct gtrace_component *comp) +{ + int ret; + + ret = gtrace_enable_component(comp); + if (ret) + dev_err(&comp->dev, "failed to start rvtrace funnel.\n"); + + return ret; +} + +static int rvtrace_funnel_stop(struct gtrace_component *comp) +{ + struct rvtrace_funnel_priv *priv = dev_get_drvdata(&comp->dev); + int ret; + + ret = gtrace_disable_component(comp); + if (ret) { + dev_err(&comp->dev, "failed to stop rvtrace funnel.\n"); + return ret; + } + + if (priv->disinput_mask) + gtrace_write32(comp->pdata, RVTRACE_FUNNEL_DISINPUT_MASK, + RVTRACE_FUNNEL_DISINPUT_OFFSET); + + return 0; +} + +static int _rvtrace_funnel_start_port(struct gtrace_component *comp, int port) +{ + struct gtrace_platform_data *pdata = comp->pdata; + struct rvtrace_funnel_priv *priv = dev_get_drvdata(&comp->dev); + u32 disinput; + int ret; + + if (!(priv->disinput_mask & BIT(port))) + return 0; + + disinput = gtrace_read32(pdata, RVTRACE_FUNNEL_DISINPUT_OFFSET); + disinput &= ~BIT(port); + gtrace_write32(pdata, disinput, RVTRACE_FUNNEL_DISINPUT_OFFSET); + ret = gtrace_poll_bit(pdata, RVTRACE_FUNNEL_DISINPUT_OFFSET, + port, 0, comp->pdata->control_poll_timeout_usecs); + if (ret) + dev_err(&comp->dev, "failed to enable rvtrace funnel inport %d.\n", port); + + return ret; +} + +static int rvtrace_funnel_start_port(struct gtrace_component *comp, + struct gtrace_connection *in, + struct gtrace_connection *out) +{ + int ret = 0; + + if (in->dest_refcnt == 0) + ret = _rvtrace_funnel_start_port(comp, in->dest_port); + if (!ret) + in->dest_refcnt++; + + return ret; +} + +static void rvtrace_funnel_stop_port(struct gtrace_component *comp, + struct gtrace_connection *in, + struct gtrace_connection *out) +{ + /* + * Setting trFunnelEnable to 0 flushes any queued trace data to output. + * Due to this flush behavior of the RISC-V trace funnel during component + * disablement, we do not disable individual ports here. Instead, we + * defer it and disable all ports at once when the entire component is + * stopped. For now, we only need to decrement the reference count. + */ + --in->dest_refcnt; +} + +static ssize_t disinput_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + struct gtrace_component *comp = to_gtrace_component(dev); + + val = gtrace_read32(comp->pdata, RVTRACE_FUNNEL_DISINPUT_OFFSET); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); +} +static DEVICE_ATTR_RO(disinput); + +static struct attribute *rvtrace_funnel_attrs[] = { + &dev_attr_disinput.attr, + NULL, +}; + +static struct attribute_group rvtrace_funnel_group = { + .attrs = rvtrace_funnel_attrs, +}; + +const struct attribute_group *rvtrace_funnel_groups[] = { + &rvtrace_funnel_group, + NULL, +}; +EXPORT_SYMBOL_GPL(rvtrace_funnel_groups); + +static int rvtrace_funnel_probe(struct gtrace_component *comp) +{ + struct gtrace_platform_data *pdata = comp->pdata; + struct rvtrace_funnel_priv *priv; + + priv = devm_kzalloc(&comp->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + gtrace_write32(pdata, RVTRACE_FUNNEL_DISINPUT_MASK, RVTRACE_FUNNEL_DISINPUT_OFFSET); + priv->disinput_mask = gtrace_read32(pdata, RVTRACE_FUNNEL_DISINPUT_OFFSET); + dev_set_drvdata(&comp->dev, priv); + return 0; +} + +static const struct gtrace_component_id rvtrace_funnel_ids[] = { + { .type = GTRACE_RVTRACE_FUNNEL, + .version = rvtrace_component_mkversion(1, 0), }, + {}, +}; + +static struct gtrace_driver rvtrace_funnel_driver = { + .id_table = rvtrace_funnel_ids, + .start = rvtrace_funnel_start, + .stop = rvtrace_funnel_stop, + .start_port = rvtrace_funnel_start_port, + .stop_port = rvtrace_funnel_stop_port, + .probe = rvtrace_funnel_probe, + .driver = { + .name = "rvtrace-funnel", + }, +}; + +static int __init rvtrace_funnel_init(void) +{ + return gtrace_register_driver(&rvtrace_funnel_driver); +} + +static void __exit rvtrace_funnel_exit(void) +{ + gtrace_unregister_driver(&rvtrace_funnel_driver); +} + +module_init(rvtrace_funnel_init); +module_exit(rvtrace_funnel_exit); + +MODULE_AUTHOR("Zane Leung"); +MODULE_DESCRIPTION("RISC-V Trace Funnel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwtracing/gtrace/rvtrace-funnel.h b/drivers/hwtracing/gtrace/rvtrace-funnel.h new file mode 100644 index 000000000000..41547c8971ba --- /dev/null +++ b/drivers/hwtracing/gtrace/rvtrace-funnel.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026 Spacemit Technologies, Inc. + */ + +#ifndef __RVTRACE_HENNEL_H__ +#define __RVTRACE_HENNEL_H__ + +#define RVTRACE_FUNNEL_DISINPUT_OFFSET 0x008 +#define RVTRACE_FUNNEL_DISINPUT_MASK 0xffff + +/** + * struct rvtrace_encoder_priv - Private driver data for the rvtrace funnel + * @disinput_mask: Bitmask of inputs supporting disable. + */ + +struct rvtrace_funnel_priv { + u32 disinput_mask; +}; + +#endif /* __RVTRACE_ENCODER_H__ */ diff --git a/drivers/hwtracing/gtrace/rvtrace-platform.c b/drivers/hwtracing/gtrace/rvtrace-platform.c index 64496a638359..ad8daa06e13e 100644 --- a/drivers/hwtracing/gtrace/rvtrace-platform.c +++ b/drivers/hwtracing/gtrace/rvtrace-platform.c @@ -133,6 +133,9 @@ static int rvtrace_platform_probe(struct platform_device *pdev) case GTRACE_RVTRACE_ENCODER: pdata->groups = rvtrace_encoder_groups; break; + case GTRACE_RVTRACE_FUNNEL: + pdata->groups = rvtrace_funnel_groups; + break; default: pdata->groups = NULL; } diff --git a/drivers/hwtracing/gtrace/rvtrace.h b/drivers/hwtracing/gtrace/rvtrace.h index fee8e2e4f42a..a345ba110267 100644 --- a/drivers/hwtracing/gtrace/rvtrace.h +++ b/drivers/hwtracing/gtrace/rvtrace.h @@ -120,5 +120,6 @@ extern const struct gtrace_hw_ops rvtrace_hw_ops;
/* Sysfs attribute groups for RISC-V trace components */ extern const struct attribute_group *rvtrace_encoder_groups[]; +extern const struct attribute_group *rvtrace_funnel_groups[];
#endif /* __LINUX_RVTRACE_H__ */