[PATCH v5 02/14] hwtracing: gtrace: Initial implementation of gtrace framework
Mayuresh Chitale
mayuresh.chitale at oss.qualcomm.com
Mon Aug 10 08:22:04 PDT 2026
From: Anup Patel <anup.patel at oss.qualcomm.com>
Implement a generic and architecture neutral trace framework (gtrace)
in which trace components are organized in a graph-like topology. The
framework provides a bus for the trace components along with helpers to
register components, build the topology and access the hardware registers.
Co-developed-by: Mayuresh Chitale <mayuresh.chitale at oss.qualcomm.com>
Signed-off-by: Mayuresh Chitale <mayuresh.chitale at oss.qualcomm.com>
Signed-off-by: Anup Patel <anup.patel at oss.qualcomm.com>
---
drivers/Makefile | 1 +
drivers/hwtracing/Kconfig | 2 +
drivers/hwtracing/gtrace/Kconfig | 15 +
drivers/hwtracing/gtrace/Makefile | 4 +
drivers/hwtracing/gtrace/gtrace-core.c | 442 +++++++++++++++++++++++++
include/linux/gtrace.h | 266 +++++++++++++++
6 files changed, 730 insertions(+)
create mode 100644 drivers/hwtracing/gtrace/Kconfig
create mode 100644 drivers/hwtracing/gtrace/Makefile
create mode 100644 drivers/hwtracing/gtrace/gtrace-core.c
create mode 100644 include/linux/gtrace.h
diff --git a/drivers/Makefile b/drivers/Makefile
index 0841ea851847..e018cc915f62 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-y += hwtracing/intel_th/
obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_HISI_PTT) += hwtracing/ptt/
+obj-$(CONFIG_GTRACE) += hwtracing/gtrace/
obj-y += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig
index 911ee977103c..ef53f5c8429f 100644
--- a/drivers/hwtracing/Kconfig
+++ b/drivers/hwtracing/Kconfig
@@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/hwtracing/ptt/Kconfig"
+source "drivers/hwtracing/gtrace/Kconfig"
+
endmenu
diff --git a/drivers/hwtracing/gtrace/Kconfig b/drivers/hwtracing/gtrace/Kconfig
new file mode 100644
index 000000000000..bcc3b4169a76
--- /dev/null
+++ b/drivers/hwtracing/gtrace/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig GTRACE
+ tristate "Generic Hardware Trace Support"
+ depends on OF
+ help
+ This framework provides an architecture-neutral kernel interface
+ for hardware trace drivers. It builds a topological view of the
+ trace components and configures the correct series of components
+ when trace is enabled. It is intended to be shared across RISC-V
+ and ARM trace components so that a single trace path may span
+ components of different architectures.
+
+ To compile this driver as a module, choose M here: the module
+ will be called gtrace.
diff --git a/drivers/hwtracing/gtrace/Makefile b/drivers/hwtracing/gtrace/Makefile
new file mode 100644
index 000000000000..3f90fb99c514
--- /dev/null
+++ b/drivers/hwtracing/gtrace/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_GTRACE) += gtrace.o
+gtrace-y := gtrace-core.o
diff --git a/drivers/hwtracing/gtrace/gtrace-core.c b/drivers/hwtracing/gtrace/gtrace-core.c
new file mode 100644
index 000000000000..97398f2b7cbb
--- /dev/null
+++ b/drivers/hwtracing/gtrace/gtrace-core.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ *
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/xarray.h>
+#include <linux/gtrace.h>
+
+/* Mutex to serialize component registration/unregistration */
+static DEFINE_MUTEX(gtrace_mutex);
+
+/* Per-CPU source instances */
+static DEFINE_PER_CPU(struct gtrace_component *, gtrace_cpu_source_comp);
+
+/* Component type based id generator */
+struct gtrace_type_idx {
+ /* Lock to protect the type ID generator */
+ struct mutex lock;
+ struct xarray xa;
+};
+
+/* Array of component type based id generator */
+static struct gtrace_type_idx gtrace_type_idx_array[GTRACE_COMPONENT_TYPE_MAX];
+
+static int gtrace_alloc_type_idx(struct gtrace_component *comp)
+{
+ struct gtrace_type_idx *gidx;
+ u32 idx;
+ int ret;
+
+ if (comp->id.type >= GTRACE_COMPONENT_TYPE_MAX)
+ return -EINVAL;
+
+ gidx = >race_type_idx_array[comp->id.type];
+ mutex_lock(&gidx->lock);
+ ret = xa_alloc(&gidx->xa, &idx, comp, xa_limit_32b, GFP_KERNEL);
+ mutex_unlock(&gidx->lock);
+ if (ret)
+ return ret;
+
+ comp->type_idx = idx;
+ return 0;
+}
+
+static void gtrace_free_type_idx(struct gtrace_component *comp)
+{
+ struct gtrace_type_idx *gidx;
+
+ if (comp->id.type >= GTRACE_COMPONENT_TYPE_MAX)
+ return;
+
+ gidx = >race_type_idx_array[comp->id.type];
+ mutex_lock(&gidx->lock);
+ xa_erase(&gidx->xa, comp->type_idx);
+ mutex_unlock(&gidx->lock);
+}
+
+static void __init gtrace_init_type_idx(void)
+{
+ struct gtrace_type_idx *gidx;
+ int i;
+
+ for (i = 0; i < GTRACE_COMPONENT_TYPE_MAX; i++) {
+ gidx = >race_type_idx_array[i];
+ mutex_init(&gidx->lock);
+ xa_init_flags(&gidx->xa, XA_FLAGS_ALLOC);
+ }
+}
+
+const struct gtrace_component_id *gtrace_match_id(struct gtrace_component *comp,
+ const struct gtrace_component_id *ids)
+{
+ const struct gtrace_component_id *id;
+
+ for (id = ids; id->version; id++) {
+ if (comp->id.type != id->type)
+ continue;
+
+ return id;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gtrace_match_id);
+
+static int gtrace_match_device(struct device *dev, const struct device_driver *drv)
+{
+ const struct gtrace_driver *gtdrv = to_gtrace_driver(drv);
+ struct gtrace_component *comp = to_gtrace_component(dev);
+
+ return gtrace_match_id(comp, gtdrv->id_table) ? 1 : 0;
+}
+
+static int gtrace_probe(struct device *dev)
+{
+ const struct gtrace_driver *gtdrv = to_gtrace_driver(dev->driver);
+ struct gtrace_component *comp = to_gtrace_component(dev);
+ int ret = 0;
+
+ if (gtdrv->probe)
+ ret = gtdrv->probe(comp);
+
+ if (!ret)
+ comp->ready = true;
+
+ return ret;
+}
+
+static void gtrace_remove(struct device *dev)
+{
+ const struct gtrace_driver *gtdrv = to_gtrace_driver(dev->driver);
+ struct gtrace_component *comp = to_gtrace_component(dev);
+
+ comp->ready = false;
+ if (gtdrv->remove)
+ gtdrv->remove(comp);
+}
+
+static const struct bus_type gtrace_bustype = {
+ .name = "gtrace",
+ .match = gtrace_match_device,
+ .probe = gtrace_probe,
+ .remove = gtrace_remove,
+};
+
+struct gtrace_fwnode_match_data {
+ struct fwnode_handle *fwnode;
+ struct gtrace_component *match;
+};
+
+static int gtrace_match_fwnode(struct device *dev, void *data)
+{
+ struct gtrace_component *comp = to_gtrace_component(dev);
+ struct gtrace_fwnode_match_data *d = data;
+
+ if (device_match_fwnode(&comp->dev, d->fwnode)) {
+ d->match = comp;
+ return 1;
+ }
+
+ return 0;
+}
+
+struct gtrace_component *gtrace_find_by_fwnode(struct fwnode_handle *fwnode)
+{
+ struct gtrace_fwnode_match_data d = { .fwnode = fwnode, .match = NULL };
+ int ret;
+
+ ret = bus_for_each_dev(>race_bustype, NULL, &d, gtrace_match_fwnode);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return d.match;
+}
+EXPORT_SYMBOL_GPL(gtrace_find_by_fwnode);
+
+int gtrace_poll_bit(struct gtrace_platform_data *pdata, int offset,
+ int bit, int bitval, int timeout)
+{
+ u32 val;
+
+ while (timeout--) {
+ val = gtrace_read32(pdata, offset);
+ if (((val >> bit) & 0x1) == bitval)
+ break;
+ udelay(1);
+ }
+
+ return (timeout < 0) ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(gtrace_poll_bit);
+
+int gtrace_enable_component(struct gtrace_component *comp)
+{
+ const struct gtrace_hw_ops *ops = comp->pdata->hw_ops;
+
+ if (!ops || !ops->enable)
+ return -EOPNOTSUPP;
+
+ return ops->enable(comp->pdata);
+}
+EXPORT_SYMBOL_GPL(gtrace_enable_component);
+
+int gtrace_disable_component(struct gtrace_component *comp)
+{
+ const struct gtrace_hw_ops *ops = comp->pdata->hw_ops;
+
+ if (!ops || !ops->disable)
+ return -EOPNOTSUPP;
+
+ return ops->disable(comp->pdata);
+}
+EXPORT_SYMBOL_GPL(gtrace_disable_component);
+
+int gtrace_reset_component(struct gtrace_component *comp)
+{
+ const struct gtrace_hw_ops *ops = comp->pdata->hw_ops;
+
+ if (!ops || !ops->reset)
+ return -EOPNOTSUPP;
+
+ return ops->reset(comp->pdata);
+}
+EXPORT_SYMBOL_GPL(gtrace_reset_component);
+
+struct gtrace_component *gtrace_cpu_source(unsigned int cpu)
+{
+ if (!cpu_present(cpu))
+ return NULL;
+
+ return per_cpu(gtrace_cpu_source_comp, cpu);
+}
+EXPORT_SYMBOL_GPL(gtrace_cpu_source);
+
+static int gtrace_cleanup_inconn(struct device *dev, void *data)
+{
+ struct gtrace_component *comp = to_gtrace_component(dev);
+ struct gtrace_platform_data *pdata = comp->pdata;
+ struct gtrace_connection *conn = data;
+ int i;
+
+ if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+ for (i = 0; i < pdata->nr_inconns; i++) {
+ if (pdata->inconns[i] != conn)
+ continue;
+ pdata->inconns[i] = NULL;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void gtrace_cleanup_inconns_from_outconns(struct gtrace_component *comp)
+{
+ struct gtrace_platform_data *pdata = comp->pdata;
+ struct gtrace_connection *conn;
+ int i;
+
+ lockdep_assert_held(>race_mutex);
+
+ for (i = 0; i < pdata->nr_outconns; i++) {
+ conn = pdata->outconns[i];
+ bus_for_each_dev(>race_bustype, NULL, conn, gtrace_cleanup_inconn);
+ }
+}
+
+static int gtrace_setup_inconn(struct device *dev, void *data)
+{
+ struct gtrace_component *comp = to_gtrace_component(dev);
+ struct gtrace_platform_data *pdata = comp->pdata;
+ struct gtrace_connection *conn = data;
+ int i;
+
+ if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) {
+ for (i = 0; i < pdata->nr_inconns; i++) {
+ if (pdata->inconns[i])
+ continue;
+ pdata->inconns[i] = conn;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gtrace_setup_inconns_from_outconns(struct gtrace_component *comp)
+{
+ struct gtrace_platform_data *pdata = comp->pdata;
+ struct gtrace_connection *conn;
+ int i, ret;
+
+ lockdep_assert_held(>race_mutex);
+
+ for (i = 0; i < pdata->nr_outconns; i++) {
+ conn = pdata->outconns[i];
+ ret = bus_for_each_dev(>race_bustype, NULL, conn, gtrace_setup_inconn);
+ if (ret < 0) {
+ gtrace_cleanup_inconns_from_outconns(comp);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void gtrace_component_release(struct device *dev)
+{
+ struct gtrace_component *comp = to_gtrace_component(dev);
+
+ fwnode_handle_put(comp->dev.fwnode);
+ gtrace_free_type_idx(comp);
+ kfree(comp);
+}
+
+struct gtrace_component *gtrace_register_component(struct gtrace_component_id *id,
+ const char *name,
+ struct gtrace_platform_data *pdata)
+{
+ struct gtrace_connection *conn;
+ struct gtrace_component *comp;
+ int i, ret = 0;
+
+ if (!id || id->type >= GTRACE_COMPONENT_TYPE_MAX) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (!pdata || !pdata->dev) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ for (i = 0; i < pdata->nr_inconns; i++) {
+ if (pdata->inconns[i]) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ for (i = 0; i < pdata->nr_outconns; i++) {
+ conn = pdata->outconns[i];
+ if (!conn || conn->src_port < 0 || conn->src_comp ||
+ !device_match_fwnode(pdata->dev, conn->src_fwnode) ||
+ conn->dest_port < 0 || !conn->dest_fwnode || !conn->dest_comp) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ if (pdata->bound_cpu >= 0 && !cpu_present(pdata->bound_cpu)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ comp->pdata = pdata;
+ comp->id = *id;
+ ret = gtrace_alloc_type_idx(comp);
+ if (ret) {
+ kfree(comp);
+ goto err_out;
+ }
+
+ comp->dev.parent = pdata->dev;
+ comp->dev.coherent_dma_mask = pdata->dev->coherent_dma_mask;
+ comp->dev.release = gtrace_component_release;
+ comp->dev.bus = >race_bustype;
+ comp->dev.fwnode = fwnode_handle_get(dev_fwnode(pdata->dev));
+ dev_set_name(&comp->dev, "%s-%u", name ? name : "comp", comp->type_idx);
+
+ mutex_lock(>race_mutex);
+
+ ret = device_register(&comp->dev);
+ if (ret) {
+ put_device(&comp->dev);
+ goto err_out_unlock;
+ }
+
+ for (i = 0; i < pdata->nr_outconns; i++) {
+ conn = pdata->outconns[i];
+ conn->src_comp = comp;
+ }
+
+ ret = gtrace_setup_inconns_from_outconns(comp);
+ if (ret < 0) {
+ device_unregister(&comp->dev);
+ goto err_out_unlock;
+ }
+
+ if (comp->pdata->bound_cpu >= 0) {
+ gtrace_get_component(comp);
+ per_cpu(gtrace_cpu_source_comp, comp->pdata->bound_cpu) = comp;
+ }
+
+ mutex_unlock(>race_mutex);
+
+ return comp;
+
+err_out_unlock:
+ mutex_unlock(>race_mutex);
+err_out:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(gtrace_register_component);
+
+void gtrace_unregister_component(struct gtrace_component *comp)
+{
+ struct gtrace_component *c;
+
+ mutex_lock(>race_mutex);
+
+ if (comp->pdata->bound_cpu >= 0) {
+ c = per_cpu(gtrace_cpu_source_comp, comp->pdata->bound_cpu);
+ per_cpu(gtrace_cpu_source_comp, comp->pdata->bound_cpu) = NULL;
+ gtrace_put_component(c);
+ }
+
+ gtrace_cleanup_inconns_from_outconns(comp);
+ device_unregister(&comp->dev);
+
+ mutex_unlock(>race_mutex);
+}
+EXPORT_SYMBOL_GPL(gtrace_unregister_component);
+
+int __gtrace_register_driver(struct module *owner, struct gtrace_driver *gtdrv)
+{
+ gtdrv->driver.owner = owner;
+ gtdrv->driver.bus = >race_bustype;
+
+ return driver_register(>drv->driver);
+}
+EXPORT_SYMBOL_GPL(__gtrace_register_driver);
+
+static int __init gtrace_init(void)
+{
+ gtrace_init_type_idx();
+
+ return bus_register(>race_bustype);
+}
+
+static void __exit gtrace_exit(void)
+{
+ bus_unregister(>race_bustype);
+}
+
+subsys_initcall(gtrace_init);
+module_exit(gtrace_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic hardware trace (gtrace) framework core");
diff --git a/include/linux/gtrace.h b/include/linux/gtrace.h
new file mode 100644
index 000000000000..e8ca2bd46e67
--- /dev/null
+++ b/include/linux/gtrace.h
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+
+#ifndef __LINUX_GTRACE_H__
+#define __LINUX_GTRACE_H__
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/types.h>
+
+/*
+ * Global list of trace component types across all architectures. Each backend
+ * uses the entries relevant to it.
+ */
+enum gtrace_component_type {
+ GTRACE_RVTRACE_ENCODER,
+ GTRACE_RVTRACE_FUNNEL,
+ GTRACE_RVTRACE_RAMSINK,
+ GTRACE_RVTRACE_PIBSINK,
+ GTRACE_RVTRACE_ATBBRIDGE,
+ GTRACE_COMPONENT_TYPE_MAX
+};
+
+/* Supported usage modes for trace components */
+enum gtrace_component_mode {
+ GTRACE_COMPONENT_MODE_PERF,
+ GTRACE_COMPONENT_MODE_MAX
+};
+
+/**
+ * struct gtrace_connection - Physical connection between two trace components.
+ * @src_port: A connection's source port number.
+ * @src_fwnode: Source component's fwnode handle.
+ * @src_comp: Source component's pointer.
+ * @dest_port: A connection's destination port number.
+ * @dest_fwnode: Destination component's fwnode handle.
+ * @dest_comp: Destination component's pointer.
+ */
+struct gtrace_connection {
+ int src_port;
+ struct fwnode_handle *src_fwnode;
+ int dest_port;
+ struct fwnode_handle *dest_fwnode;
+ struct gtrace_component *src_comp;
+ struct gtrace_component *dest_comp;
+};
+
+struct gtrace_platform_data;
+
+/**
+ * struct gtrace_hw_ops - Architecture-specific low level control of a component.
+ * @enable: Enable the component's hardware block.
+ * @disable: Disable the component's hardware block.
+ * @reset: Reset the component's hardware block.
+ *
+ * These operate on the component's platform data rather than a full
+ * gtrace_component so that they can be invoked before the component is
+ * registered with the gtrace core (e.g. to reset the hardware during probe).
+ *
+ * These abstract the register-level programming that differs between
+ * architectures (RISC-V Trace Control registers vs ARM CoreSight, etc.). The
+ * core never touches component registers directly; it invokes these ops.
+ */
+struct gtrace_hw_ops {
+ int (*enable)(struct gtrace_platform_data *pdata);
+ int (*disable)(struct gtrace_platform_data *pdata);
+ int (*reset)(struct gtrace_platform_data *pdata);
+};
+
+/**
+ * struct gtrace_platform_data - Platform-level data for a trace component
+ * discovered from DT or ACPI.
+ * @dev: Parent device.
+ * @impid: Opaque, architecture-specific implementation ID.
+ * @hw_ops: Architecture-specific low level control ops.
+ * @io_mem: Flag showing whether component registers are memory mapped.
+ * @base: If io_mem == true then base address of the mapped registers.
+ * @read: If io_mem == false then read register from the given offset.
+ * @write: If io_mem == false then write register to the given offset.
+ * @bound_cpu: CPU to which the component is bound, or -1 if unbound. For a
+ * source component bound to a CPU this must not be -1.
+ * @control_poll_timeout_usecs: Delay in usecs when polling control bits.
+ * @nr_inconns: Number of input connections.
+ * @inconns: Array of pointers to input connections.
+ * @nr_outconns: Number of output connections.
+ * @outconns: Array of pointers to output connections.
+ */
+struct gtrace_platform_data {
+ struct device *dev;
+
+ u32 impid;
+
+ const struct gtrace_hw_ops *hw_ops;
+
+ bool io_mem;
+ union {
+ void __iomem *base;
+ struct {
+ u32 (*read)(struct gtrace_platform_data *pdata,
+ u32 offset, bool relaxed);
+ void (*write)(struct gtrace_platform_data *pdata,
+ u32 val, u32 offset, bool relaxed);
+ };
+ };
+
+ int bound_cpu;
+
+ /* Delay in microseconds when polling control register bits */
+ int control_poll_timeout_usecs;
+
+ /*
+ * Platform driver must only populate empty pointer array without
+ * any actual input connections.
+ */
+ unsigned int nr_inconns;
+ struct gtrace_connection **inconns;
+
+ /*
+ * Platform driver must fully populate pointer array with individual
+ * array elements pointing to actual output connections. The src_comp
+ * of each output connection is automatically updated at the time of
+ * registering component.
+ */
+ unsigned int nr_outconns;
+ struct gtrace_connection **outconns;
+};
+
+static inline u32 gtrace_read32(struct gtrace_platform_data *pdata, u32 offset)
+{
+ if (likely(pdata->io_mem))
+ return readl(pdata->base + offset);
+
+ return pdata->read(pdata, offset, false);
+}
+
+static inline u32 gtrace_relaxed_read32(struct gtrace_platform_data *pdata, u32 offset)
+{
+ if (likely(pdata->io_mem))
+ return readl_relaxed(pdata->base + offset);
+
+ return pdata->read(pdata, offset, true);
+}
+
+static inline void gtrace_write32(struct gtrace_platform_data *pdata, u32 val, u32 offset)
+{
+ if (likely(pdata->io_mem))
+ writel(val, pdata->base + offset);
+ else
+ pdata->write(pdata, val, offset, false);
+}
+
+static inline void gtrace_relaxed_write32(struct gtrace_platform_data *pdata,
+ u32 val, u32 offset)
+{
+ if (likely(pdata->io_mem))
+ writel_relaxed(val, pdata->base + offset);
+ else
+ pdata->write(pdata, val, offset, true);
+}
+
+static inline bool gtrace_is_source(struct gtrace_platform_data *pdata)
+{
+ return !pdata->nr_inconns ? true : false;
+}
+
+static inline bool gtrace_is_sink(struct gtrace_platform_data *pdata)
+{
+ return !pdata->nr_outconns ? true : false;
+}
+
+/**
+ * struct gtrace_component_id - Details to identify or match a trace component.
+ * @type: Component type from the global gtrace_component_type list.
+ * @version: Architecture-specific version (opaque to the core).
+ * @data: Data pointer for driver use.
+ */
+struct gtrace_component_id {
+ enum gtrace_component_type type;
+ u32 version;
+ void *data;
+};
+
+/**
+ * struct gtrace_component - Representation of a trace component.
+ * @pdata: Pointer to underlying platform data.
+ * @id: Details to match the component.
+ * @type_idx: Unique number based on component type.
+ * @dev: Device instance.
+ * @ready: Flag showing whether the driver was probed successfully.
+ */
+struct gtrace_component {
+ struct gtrace_platform_data *pdata;
+ struct gtrace_component_id id;
+ u32 type_idx;
+ struct device dev;
+ bool ready;
+};
+
+#define to_gtrace_component(__dev) container_of_const(__dev, struct gtrace_component, dev)
+
+static inline void gtrace_get_component(struct gtrace_component *comp)
+{
+ get_device(&comp->dev);
+}
+
+static inline void gtrace_put_component(struct gtrace_component *comp)
+{
+ put_device(&comp->dev);
+}
+
+const struct gtrace_component_id *gtrace_match_id(struct gtrace_component *comp,
+ const struct gtrace_component_id *ids);
+struct gtrace_component *gtrace_find_by_fwnode(struct fwnode_handle *fwnode);
+
+int gtrace_poll_bit(struct gtrace_platform_data *pdata, int offset,
+ int bit, int bitval, int timeout);
+int gtrace_enable_component(struct gtrace_component *comp);
+int gtrace_disable_component(struct gtrace_component *comp);
+int gtrace_reset_component(struct gtrace_component *comp);
+
+struct gtrace_component *gtrace_cpu_source(unsigned int cpu);
+
+struct gtrace_component *gtrace_register_component(struct gtrace_component_id *id,
+ const char *name,
+ struct gtrace_platform_data *pdata);
+void gtrace_unregister_component(struct gtrace_component *comp);
+
+/**
+ * struct gtrace_driver - Representation of a trace driver.
+ * @id_table: Table to match components handled by the driver.
+ * @probe: Driver probe() function.
+ * @remove: Driver remove() function.
+ * @get_trace_id: Get/allocate a trace ID.
+ * @put_trace_id: Put/free a trace ID.
+ * @driver: Device driver instance.
+ */
+struct gtrace_driver {
+ const struct gtrace_component_id *id_table;
+ int (*probe)(struct gtrace_component *comp);
+ void (*remove)(struct gtrace_component *comp);
+ int (*get_trace_id)(struct gtrace_component *comp,
+ enum gtrace_component_mode mode);
+ void (*put_trace_id)(struct gtrace_component *comp,
+ enum gtrace_component_mode mode,
+ u32 trace_id);
+ struct device_driver driver;
+};
+
+#define to_gtrace_driver(__drv) \
+ ((__drv) ? container_of_const((__drv), struct gtrace_driver, driver) : NULL)
+
+int __gtrace_register_driver(struct module *owner, struct gtrace_driver *gtdrv);
+#define gtrace_register_driver(driver) __gtrace_register_driver(THIS_MODULE, driver)
+static inline void gtrace_unregister_driver(struct gtrace_driver *gtdrv)
+{
+ if (gtdrv)
+ driver_unregister(>drv->driver);
+}
+
+#endif /* __LINUX_GTRACE_H__ */
--
2.43.0
More information about the linux-riscv
mailing list