[PATCH v5 03/14] gtrace: Add RISC-V platform driver for the gtrace framework
Mayuresh Chitale <[email protected]>
| Newsgroups | org.infradead.lists.linux-riscv,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Anup Patel <[email protected]> The RISC-V trace components defined by the RISC-V Trace Control Interface Specification are memory mapped devices which are organized in a graph-like topology and described in the device tree. Add the RISC-V platform driver for the gtrace framework. The driver parses the RISC-V component topology from the device tree, discovers the type and version of each component from its IMPL register, provides the RISC-V register level control ops and registers each component with the gtrace core. Co-developed-by: Mayuresh Chitale <[email protected]> Signed-off-by: Mayuresh Chitale <[email protected]> Signed-off-by: Anup Patel <[email protected]> --- drivers/hwtracing/gtrace/Kconfig | 15 + drivers/hwtracing/gtrace/Makefile | 2 + drivers/hwtracing/gtrace/rvtrace-platform.c | 286 ++++++++++++++++++++ drivers/hwtracing/gtrace/rvtrace.h | 121 +++++++++ 4 files changed, 424 insertions(+) create mode 100644 drivers/hwtracing/gtrace/rvtrace-platform.c create mode 100644 drivers/hwtracing/gtrace/rvtrace.h diff --git a/drivers/hwtracing/gtrace/Kconfig b/drivers/hwtracing/gtrace/Kconfig index bcc3b4169a76..04986f216549 100644 --- a/drivers/hwtracing/gtrace/Kconfig +++ b/drivers/hwtracing/gtrace/Kconfig @@ -13,3 +13,18 @@ menuconfig GTRACE To compile this driver as a module, choose M here: the module will be called gtrace. + +config RVTRACE + tristate "RISC-V Trace Support" + depends on RISCV + depends on OF + depends on GTRACE + default RISCV + help + This provides the RISC-V backend for the generic hardware trace + (gtrace) framework. It parses the RISC-V trace component topology, + provides the RISC-V register-level control ops and registers each + component with the gtrace core. + + To compile this driver as a module, choose M here: the module + will be called rvtrace. diff --git a/drivers/hwtracing/gtrace/Makefile b/drivers/hwtracing/gtrace/Makefile index 3f90fb99c514..cf02316c1bbf 100644 --- a/drivers/hwtracing/gtrace/Makefile +++ b/drivers/hwtracing/gtrace/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_GTRACE) += gtrace.o gtrace-y := gtrace-core.o + +obj-$(CONFIG_RVTRACE) += rvtrace-platform.o diff --git a/drivers/hwtracing/gtrace/rvtrace-platform.c b/drivers/hwtracing/gtrace/rvtrace-platform.c new file mode 100644 index 000000000000..f34de9183d9a --- /dev/null +++ b/drivers/hwtracing/gtrace/rvtrace-platform.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + */ + +#include <linux/device.h> +#include <linux/gtrace.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/types.h> +#include "rvtrace.h" + +static int rvtrace_hw_enable(struct gtrace_platform_data *pdata) +{ + u32 val; + + val = gtrace_read32(pdata, RVTRACE_COMPONENT_CTRL_OFFSET); + val |= BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + gtrace_write32(pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET); + return gtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1, + pdata->control_poll_timeout_usecs); +} + +static int rvtrace_hw_disable(struct gtrace_platform_data *pdata) +{ + u32 val; + + val = gtrace_read32(pdata, RVTRACE_COMPONENT_CTRL_OFFSET); + val &= ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + gtrace_write32(pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET); + return gtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0, + pdata->control_poll_timeout_usecs); +} + +static int rvtrace_hw_reset(struct gtrace_platform_data *pdata) +{ + int ret; + + gtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET); + ret = gtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0, + pdata->control_poll_timeout_usecs); + if (ret) + return ret; + + gtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK, + RVTRACE_COMPONENT_CTRL_OFFSET); + return gtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1, + pdata->control_poll_timeout_usecs); +} + +const struct gtrace_hw_ops rvtrace_hw_ops = { + .enable = rvtrace_hw_enable, + .disable = rvtrace_hw_disable, + .reset = rvtrace_hw_reset, +}; + +static int rvtrace_of_parse_outconns(struct gtrace_platform_data *pdata) +{ + struct device_node *parent, *ep_node, *rep_node, *rdev_node; + struct gtrace_connection *conn; + struct of_endpoint ep, rep; + int ret = 0, i = 0; + + parent = of_get_child_by_name(dev_of_node(pdata->dev), "out-ports"); + if (!parent) + return 0; + + pdata->nr_outconns = of_graph_get_endpoint_count(parent); + pdata->outconns = devm_kcalloc(pdata->dev, pdata->nr_outconns, + sizeof(*pdata->outconns), GFP_KERNEL); + if (!pdata->outconns) { + ret = -ENOMEM; + goto done; + } + + for_each_endpoint_of_node(parent, ep_node) { + conn = devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL); + if (!conn) { + of_node_put(ep_node); + ret = -ENOMEM; + break; + } + + ret = of_graph_parse_endpoint(ep_node, &ep); + if (ret) { + of_node_put(ep_node); + break; + } + + rep_node = of_graph_get_remote_endpoint(ep_node); + if (!rep_node) { + ret = -ENODEV; + of_node_put(ep_node); + break; + } + rdev_node = of_graph_get_port_parent(rep_node); + + ret = of_graph_parse_endpoint(rep_node, &rep); + if (ret) { + of_node_put(ep_node); + of_node_put(rep_node); + of_node_put(rdev_node); + break; + } + + conn->src_port = ep.port; + conn->src_fwnode = dev_fwnode(pdata->dev); + /* The 'src_comp' is set by gtrace_register_component() */ + conn->src_comp = NULL; + conn->dest_port = rep.port; + conn->dest_fwnode = of_fwnode_handle(rdev_node); + fwnode_handle_get(conn->dest_fwnode); + conn->dest_comp = gtrace_find_by_fwnode(conn->dest_fwnode); + if (!conn->dest_comp) { + ret = -EPROBE_DEFER; + of_node_put(ep_node); + of_node_put(rep_node); + of_node_put(rdev_node); + break; + } + + pdata->outconns[i] = conn; + i++; + } + +done: + if (ret) { + for (i = 0; i < pdata->nr_outconns && pdata->outconns; i++) { + conn = pdata->outconns[i]; + if (conn && conn->dest_fwnode) + fwnode_handle_put(conn->dest_fwnode); + } + } + of_node_put(parent); + return ret; +} + +static int rvtrace_of_parse_inconns(struct gtrace_platform_data *pdata) +{ + struct device_node *parent; + int ret = 0; + + parent = of_get_child_by_name(dev_of_node(pdata->dev), "in-ports"); + if (!parent) + return 0; + + pdata->nr_inconns = of_graph_get_endpoint_count(parent); + pdata->inconns = devm_kcalloc(pdata->dev, pdata->nr_inconns, + sizeof(*pdata->inconns), GFP_KERNEL); + if (!pdata->inconns) + ret = -ENOMEM; + + of_node_put(parent); + return ret; +} + +static int rvtrace_platform_probe(struct platform_device *pdev) +{ + struct gtrace_platform_data *pdata; + struct device *dev = &pdev->dev; + struct gtrace_component_id id; + struct gtrace_component *comp; + u32 impl, type, major, minor; + struct device_node *node; + struct resource *res; + int gtype; + int ret; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->dev = dev; + pdata->impid = RVTRACE_COMPONENT_IMPID_UNKNOWN; + pdata->hw_ops = &rvtrace_hw_ops; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + pdata->io_mem = true; + pdata->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!pdata->base) + return dev_err_probe(dev, -ENOMEM, "failed to ioremap %pR\n", res); + + pdata->bound_cpu = -1; + node = of_parse_phandle(dev_of_node(dev), "cpus", 0); + if (node) { + ret = of_cpu_node_to_id(node); + of_node_put(node); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get CPU id for %pOF\n", node); + pdata->bound_cpu = ret; + } + + /* Default control poll timeout */ + pdata->control_poll_timeout_usecs = 10; + + ret = rvtrace_of_parse_outconns(pdata); + if (ret) + return dev_err_probe(dev, ret, "failed to parse output connections\n"); + + ret = rvtrace_of_parse_inconns(pdata); + if (ret) + return dev_err_probe(dev, ret, "failed to parse input connections\n"); + + /* Reset the component before it is registered with the gtrace core. */ + ret = rvtrace_hw_reset(pdata); + if (ret) + return dev_err_probe(dev, ret, "failed to reset component\n"); + + impl = gtrace_read32(pdata, RVTRACE_COMPONENT_IMPL_OFFSET); + type = (impl >> RVTRACE_COMPONENT_IMPL_TYPE_SHIFT) & + RVTRACE_COMPONENT_IMPL_TYPE_MASK; + major = (impl >> RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK; + minor = (impl >> RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMINOR_MASK; + + gtype = rvtrace_type_to_gtrace(type); + if (gtype < 0) + return dev_err_probe(dev, -ENODEV, "unsupported component type %u\n", type); + + id.type = gtype; + id.version = rvtrace_component_mkversion(major, minor); + + comp = gtrace_register_component(&id, rvtrace_type_name(gtype), pdata); + if (IS_ERR(comp)) + return PTR_ERR(comp); + + platform_set_drvdata(pdev, comp); + return 0; +} + +static void rvtrace_platform_remove(struct platform_device *pdev) +{ + struct gtrace_component *comp = platform_get_drvdata(pdev); + struct gtrace_platform_data *pdata = comp->pdata; + struct gtrace_connection *conn; + int i; + + for (i = 0; i < pdata->nr_outconns; i++) { + conn = pdata->outconns[i]; + if (conn && conn->dest_fwnode) + fwnode_handle_put(conn->dest_fwnode); + } + + gtrace_unregister_component(comp); +} + +static const struct of_device_id rvtrace_platform_match[] = { + { .compatible = "riscv,trace-component" }, + {} +}; + +static struct platform_driver rvtrace_platform_driver = { + .driver = { + .name = "rvtrace", + .of_match_table = rvtrace_platform_match, + }, + .probe = rvtrace_platform_probe, + .remove = rvtrace_platform_remove, +}; + +static int __init rvtrace_platform_init(void) +{ + return platform_driver_register(&rvtrace_platform_driver); +} + +static void __exit rvtrace_platform_exit(void) +{ + platform_driver_unregister(&rvtrace_platform_driver); +} + +module_init(rvtrace_platform_init); +module_exit(rvtrace_platform_exit); + +MODULE_AUTHOR("Anup Patel"); +MODULE_DESCRIPTION("RISC-V backend for the generic trace framework"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwtracing/gtrace/rvtrace.h b/drivers/hwtracing/gtrace/rvtrace.h new file mode 100644 index 000000000000..e6be1ce303cc --- /dev/null +++ b/drivers/hwtracing/gtrace/rvtrace.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + * + * RISC-V trace component register layout and helpers for the RISC-V backend + * of the generic trace (gtrace) framework. These definitions are specific to + * the RISC-V Trace Control Interface and must not be used by the generic core. + */ + +#ifndef __LINUX_RVTRACE_H__ +#define __LINUX_RVTRACE_H__ + +#include <linux/gtrace.h> +#include <linux/types.h> + +/* Control register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_CTRL_OFFSET 0x000 +#define RVTRACE_COMPONENT_CTRL_ACTIVE_MASK 0x1 +#define RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT 0 +#define RVTRACE_COMPONENT_CTRL_ENABLE_MASK 0x1 +#define RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT 1 +#define RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT 3 + +/* Implementation register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_IMPL_OFFSET 0x004 +#define RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT 0 +#define RVTRACE_COMPONENT_IMPL_VERMINOR_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT 4 +#define RVTRACE_COMPONENT_IMPL_TYPE_MASK 0xf +#define RVTRACE_COMPONENT_IMPL_TYPE_SHIFT 8 + +/* Component types defined by the RISC-V Trace Control Interface */ +enum rvtrace_component_type { + RVTRACE_COMPONENT_TYPE_RESV0, + RVTRACE_COMPONENT_TYPE_ENCODER, /* 0x1 */ + RVTRACE_COMPONENT_TYPE_RESV2, + RVTRACE_COMPONENT_TYPE_RESV3, + RVTRACE_COMPONENT_TYPE_RESV4, + RVTRACE_COMPONENT_TYPE_RESV5, + RVTRACE_COMPONENT_TYPE_RESV6, + RVTRACE_COMPONENT_TYPE_RESV7, + RVTRACE_COMPONENT_TYPE_FUNNEL, /* 0x8 */ + RVTRACE_COMPONENT_TYPE_RAMSINK, /* 0x9 */ + RVTRACE_COMPONENT_TYPE_PIBSINK, /* 0xA */ + RVTRACE_COMPONENT_TYPE_RESV11, + RVTRACE_COMPONENT_TYPE_RESV12, + RVTRACE_COMPONENT_TYPE_RESV13, + RVTRACE_COMPONENT_TYPE_ATBBRIDGE, /* 0xE */ + RVTRACE_COMPONENT_TYPE_RESV15, + RVTRACE_COMPONENT_TYPE_MAX +}; + +/* + * Possible component implementation IDs discovered from DT or ACPI + * shared across the RISC-V trace drivers to infer trace parameters, + * quirks, and work-arounds. These component implementation IDs are + * internal to Linux and must not be exposed to user-space. + * + * The component implementation ID should be named as follows: + * RVTRACE_COMPONENT_IMPID_<vendor>_<part> + */ +enum rvtrace_component_impid { + RVTRACE_COMPONENT_IMPID_UNKNOWN, + RVTRACE_COMPONENT_IMPID_MAX +}; + +#define rvtrace_component_version_major(__version) \ + (((__version) >> 16) & 0xffff) +#define rvtrace_component_version_minor(__version) \ + ((__version) & 0xffff) +#define rvtrace_component_mkversion(__major, __minor) \ + ((((__major) & 0xffff) << 16) | ((__minor) & 0xffff)) + +/* Map a RISC-V hardware component type to the global gtrace type */ +static inline int rvtrace_type_to_gtrace(enum rvtrace_component_type type) +{ + switch (type) { + case RVTRACE_COMPONENT_TYPE_ENCODER: + return GTRACE_RVTRACE_ENCODER; + case RVTRACE_COMPONENT_TYPE_FUNNEL: + return GTRACE_RVTRACE_FUNNEL; + case RVTRACE_COMPONENT_TYPE_RAMSINK: + return GTRACE_RVTRACE_RAMSINK; + case RVTRACE_COMPONENT_TYPE_PIBSINK: + return GTRACE_RVTRACE_PIBSINK; + case RVTRACE_COMPONENT_TYPE_ATBBRIDGE: + return GTRACE_RVTRACE_ATBBRIDGE; + default: + return -EINVAL; + } +} + +static inline const char *rvtrace_type_name(enum gtrace_component_type type) +{ + switch (type) { + case GTRACE_RVTRACE_ENCODER: + return "encoder"; + case GTRACE_RVTRACE_FUNNEL: + return "funnel"; + case GTRACE_RVTRACE_RAMSINK: + return "ramsink"; + case GTRACE_RVTRACE_PIBSINK: + return "pibsink"; + case GTRACE_RVTRACE_ATBBRIDGE: + return "atbbridge"; + default: + return NULL; + } +} + +static inline int rvtrace_comp_poll_empty(struct gtrace_component *comp) +{ + return gtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_EMPTY_SHIFT, 1, + comp->pdata->control_poll_timeout_usecs); +} + +extern const struct gtrace_hw_ops rvtrace_hw_ops; + +#endif /* __LINUX_RVTRACE_H__ */ -- 2.43.0 _______________________________________________ linux-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-riscv