From nobody Wed Dec 17 17:57:43 2025 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E9A9728727F for ; Thu, 2 Oct 2025 06:08:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759385319; cv=none; b=MVWiTKB99uNawMCFdMNtAdXj26w1iiNWjJLtPo1fv4vKflRzX/3C+Q4lBtMhuV5nMMXcHkaFWiFr0d2WzbmtB7Lnnr+KuHaLiX71A3PfNkZDc8LDrUJSP7QkFDtohv4vBBKgK5Hi7HB5v7lhHm0BrBbi8077d/SWEoqRJmdGPck= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759385319; c=relaxed/simple; bh=ybQyvvm6i1y4Wa8Op9xhy2eIkespA8Nb9GMqF6JaXRg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jU14UJrG6bTQsAb0aXQvJJzahJsgy7wFYD6fJpli59pAjMr/iH20hgnMZmjxFgmUC19rMoLioAgEy2lAQ/PM8q4MvD0BpEFI5SN4/TaqUjYGj9o8aNmb1ItO6b+gVvxDWJdJ9OQnr7HdP7lvFn/Okqb6TkyEELgfMH/uLQJ201o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=ventanamicro.com; spf=pass smtp.mailfrom=ventanamicro.com; dkim=pass (2048-bit key) header.d=ventanamicro.com header.i=@ventanamicro.com header.b=eSdkix+o; arc=none smtp.client-ip=209.85.210.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=ventanamicro.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ventanamicro.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ventanamicro.com header.i=@ventanamicro.com header.b="eSdkix+o" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-781206cce18so617910b3a.0 for ; Wed, 01 Oct 2025 23:08:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ventanamicro.com; s=google; t=1759385316; x=1759990116; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=R8j8k0Vez178jFtEYMAyyi4KPe42MRS7/ciUbfnTnyc=; b=eSdkix+oGd1dz35wI2NtuYXR7t+xVA+hCziu0kf9OyQhYS0WcwBWNiweM9kN+ED/Oh MNnN/DcP6T+PfL7lsf3Kt+O7c5MAl7RbQ9NXJR0wWU2WvEnWDxeqCAOyZYJyOTpB+EuE OjH+mlZhu44Q2exETQlX7DruFpmZ3q7XhUcc9qA4Zn2Aezk8f4XzxVdvSY37fBxhl12Q DW2hYkCmqXqKRpugpTtrqs6uzKVC3blQVVQC1BtubdT4pgx6z7lasAq7ojGQPmlcdW7E Bkh0lawCKGqv/qMp+VFY2F3jqNmcKu7EDIKPhnogLmDlJqKmcYBoXzVHjL1Kx9uQwqz6 OOFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759385316; x=1759990116; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=R8j8k0Vez178jFtEYMAyyi4KPe42MRS7/ciUbfnTnyc=; b=q9vr/Xltdv1m19Uik2kucgzc/P3PCifiUw5SyjkmfeG07c63V3hNrvb3FdGlcPFvdp wehhbKLTZ6MS1/roYuKDxdmVYzlJYlRdAzbROTVLpLMbBaw+3odB3xACHCMfX6F52j2g knzwbdTJpfqSPqXcZyEYIWIzjxVMzPM7oz1jRN29nB2TtlqtRPM+tlpULGdwzkA4yzeG cNqu8M5iSCtV3Lzn2kc7xTrd0dzqVU6YqDDyn2QybcCrd8jJ2rNPieQx6EkIjZ7UmHt8 5rPDUlAx6AmEwpOQuJRuxnKeog617NGjCUbsXE+LHKsFb5gOdMzHyFdBpHUJWmy1gv0l LKgA== X-Forwarded-Encrypted: i=1; AJvYcCVVWgSC/IxgP+ewled3vxxlB6gBiFozF4TbyA1iR4TSFpFk0uiKDcFz+L9i2LoKaKbTAkIfxyN9GmS8+Kc=@vger.kernel.org X-Gm-Message-State: AOJu0YyQMSk30XHrsB0+mTRSF1bSrfQwYdLsMJTODiAwOhnHgJJOvNuV 2om6OBQ+I1rnVp9SBWFrcBfDikJnz514LIa3m315eVyAM4V71IvFr72opeSTHPV3D20= X-Gm-Gg: ASbGncvubLu+c8alqhBcKUV6fqVG2xJ7lz5BhDUol47AzjIub/WfGPCXUQWlQR28rAw ZYXUl8NBNDdebs0GNzdMyswBUMJgXSDYDl+serQSPAHN47FwLIQw/rqJiONNGQXaRSQSXMAr6Ht 24VxqMtHzIZirVBU+BTNtwB0B5+MwITS1lIHwZnFj0qe/rohRrd8gasAxQmrdT/rOvWFQ+nVutL vu957IbHfDmjNMi8pvf9QPFogurDCGiNprw/kn/2FHo4Y71tMJogZLSMy0d1tjdEvtQmQqyFvPi LwDqLwaNxm0ADseMCD8jxQWeYlRYSyrvZ/zCJeG1ueNmVxuhyQgWZ9RwEFSLO8R95R+3ajN3bPb LeUMJ+rnoUdjc8MzUO6zo8DU5IvtP0qImm9h6kMNFDbMyVpkzwefbZqqEOb08P/Q8rkCqtTbFP8 2bUvQ= X-Google-Smtp-Source: AGHT+IHs1pxgNcFj/av/XwgniPcZJfFd5oWIbk+o5Q7JRBBQbc0wPq6C26fCiOdOg6tGrPl4fClVtQ== X-Received: by 2002:a05:6a20:9f93:b0:25b:b4d0:1090 with SMTP id adf61e73a8af0-32a24dc78aamr3598056637.17.1759385315902; Wed, 01 Oct 2025 23:08:35 -0700 (PDT) Received: from localhost.localdomain ([122.171.19.158]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b6099f594afsm1205029a12.37.2025.10.01.23.08.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Oct 2025 23:08:35 -0700 (PDT) From: Anup Patel To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Greg KH , Alexander Shishkin , Ian Rogers Cc: Alexandre Ghiti , Peter Zijlstra , Ingo Molnar , Namhyung Kim , Mark Rutland , Jiri Olsa , Adrian Hunter , Liang Kan , Mayuresh Chitale , Anup Patel , Atish Patra , Andrew Jones , Sunil V L , linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Anup Patel , Mayuresh Chitale Subject: [PATCH 02/11] rvtrace: Initial implementation of driver framework Date: Thu, 2 Oct 2025 11:37:23 +0530 Message-ID: <20251002060732.100213-3-apatel@ventanamicro.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251002060732.100213-1-apatel@ventanamicro.com> References: <20251002060732.100213-1-apatel@ventanamicro.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The RISC-V Trace Control Interface Specification [1] defines a standard way of implementing RISC-V trace related modular components irrespective to underlying trace format (E-trace or N-trace). These RISC-V trace components are organized in a graph-like topology where each RISC-V hart has its own RISC-V trace encoder component. Implement a basic driver framework for RISC-V trace where RISC-V trace components are instantiated by a common platform driver and a separate RISC-V trace driver for each type of RISC-V trace component. [1] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_R= atified/RISC-V-Trace-Control-Interface.pdf Co-developed-by: Mayuresh Chitale Signed-off-by: Mayuresh Chitale Signed-off-by: Anup Patel --- drivers/Makefile | 1 + drivers/hwtracing/Kconfig | 2 + drivers/hwtracing/rvtrace/Kconfig | 16 + drivers/hwtracing/rvtrace/Makefile | 4 + drivers/hwtracing/rvtrace/rvtrace-core.c | 484 +++++++++++++++++++ drivers/hwtracing/rvtrace/rvtrace-platform.c | 174 +++++++ include/linux/rvtrace.h | 272 +++++++++++ 7 files changed, 953 insertions(+) create mode 100644 drivers/hwtracing/rvtrace/Kconfig create mode 100644 drivers/hwtracing/rvtrace/Makefile create mode 100644 drivers/hwtracing/rvtrace/rvtrace-core.c create mode 100644 drivers/hwtracing/rvtrace/rvtrace-platform.c create mode 100644 include/linux/rvtrace.h diff --git a/drivers/Makefile b/drivers/Makefile index b5749cf67044..466a55580f60 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -178,6 +178,7 @@ obj-$(CONFIG_CORESIGHT) +=3D hwtracing/coresight/ obj-y +=3D hwtracing/intel_th/ obj-$(CONFIG_STM) +=3D hwtracing/stm/ obj-$(CONFIG_HISI_PTT) +=3D hwtracing/ptt/ +obj-$(CONFIG_RVTRACE) +=3D hwtracing/rvtrace/ obj-y +=3D android/ obj-$(CONFIG_NVMEM) +=3D nvmem/ obj-$(CONFIG_FPGA) +=3D fpga/ diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig index 911ee977103c..daeb38fe332d 100644 --- a/drivers/hwtracing/Kconfig +++ b/drivers/hwtracing/Kconfig @@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig" =20 source "drivers/hwtracing/ptt/Kconfig" =20 +source "drivers/hwtracing/rvtrace/Kconfig" + endmenu diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/= Kconfig new file mode 100644 index 000000000000..f8f6feea1953 --- /dev/null +++ b/drivers/hwtracing/rvtrace/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig RVTRACE + tristate "RISC-V Trace Support" + depends on RISCV + depends on OF + default RISCV + help + This framework provides a kernel interface for the RISC-V trace + drivers (including both e-trace and n-trace). It's intended to + build a topological view of the RISC-V trace components and + configure the right series of components when trace is enabled + on a CPU. + + To compile this driver as a module, choose M here: the module + will be called rvtrace. diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace= /Makefile new file mode 100644 index 000000000000..988525a379cf --- /dev/null +++ b/drivers/hwtracing/rvtrace/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_RVTRACE) +=3D rvtrace.o +rvtrace-y :=3D rvtrace-core.o rvtrace-platform.o diff --git a/drivers/hwtracing/rvtrace/rvtrace-core.c b/drivers/hwtracing/r= vtrace/rvtrace-core.c new file mode 100644 index 000000000000..52ea931745fc --- /dev/null +++ b/drivers/hwtracing/rvtrace/rvtrace-core.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Mutex to serialize component registration/unregistration */ +static DEFINE_MUTEX(rvtrace_mutex); + +/* Per-CPU encoder instances */ +static DEFINE_PER_CPU(struct rvtrace_component *, rvtrace_cpu_encoder); + +/* Component type based id generator */ +struct rvtrace_type_idx { + /* Lock to protect the type ID generator */ + struct mutex lock; + struct idr idr; +}; + +/* Array of component type based id generator */ +static struct rvtrace_type_idx rvtrace_type_idx_array[RVTRACE_COMPONENT_TY= PE_MAX]; + +static int rvtrace_alloc_type_idx(struct rvtrace_component *comp) +{ + struct rvtrace_type_idx *rvidx =3D &rvtrace_type_idx_array[comp->id.type]; + int idx; + + mutex_lock(&rvidx->lock); + idx =3D idr_alloc(&rvidx->idr, comp, 0, 0, GFP_KERNEL); + mutex_unlock(&rvidx->lock); + if (idx < 0) + return idx; + + comp->type_idx =3D idx; + return 0; +} + +static void rvtrace_free_type_idx(struct rvtrace_component *comp) +{ + struct rvtrace_type_idx *rvidx =3D &rvtrace_type_idx_array[comp->id.type]; + + mutex_lock(&rvidx->lock); + idr_remove(&rvidx->idr, comp->type_idx); + mutex_unlock(&rvidx->lock); +} + +static void __init rvtrace_init_type_idx(void) +{ + struct rvtrace_type_idx *rvidx; + int i; + + for (i =3D 0; i < RVTRACE_COMPONENT_TYPE_MAX; i++) { + rvidx =3D &rvtrace_type_idx_array[i]; + mutex_init(&rvidx->lock); + idr_init(&rvidx->idr); + } +} + +const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_compone= nt *comp, + const struct rvtrace_component_id *ids) +{ + const struct rvtrace_component_id *id; + + for (id =3D ids; id->version && id->type; id++) { + if (comp->id.type =3D=3D id->type && + comp->id.version =3D=3D id->version) + return id; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(rvtrace_match_id); + +static int rvtrace_match_device(struct device *dev, const struct device_dr= iver *drv) +{ + const struct rvtrace_driver *rtdrv =3D to_rvtrace_driver(drv); + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + + return rvtrace_match_id(comp, rtdrv->id_table) ? 1 : 0; +} + +static int rvtrace_probe(struct device *dev) +{ + const struct rvtrace_driver *rtdrv =3D to_rvtrace_driver(dev->driver); + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + int ret =3D -ENODEV; + + if (!rtdrv->probe) + return ret; + + ret =3D rtdrv->probe(comp); + if (!ret) + comp->ready =3D true; + + return ret; +} + +static void rvtrace_remove(struct device *dev) +{ + const struct rvtrace_driver *rtdrv =3D to_rvtrace_driver(dev->driver); + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + + comp->ready =3D false; + if (rtdrv->remove) + rtdrv->remove(comp); +} + +const struct bus_type rvtrace_bustype =3D { + .name =3D "rvtrace", + .match =3D rvtrace_match_device, + .probe =3D rvtrace_probe, + .remove =3D rvtrace_remove, +}; + +struct rvtrace_fwnode_match_data { + struct fwnode_handle *fwnode; + struct rvtrace_component *match; +}; + +static int rvtrace_match_fwnode(struct device *dev, void *data) +{ + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + struct rvtrace_fwnode_match_data *d =3D data; + + if (device_match_fwnode(&comp->dev, d->fwnode)) { + d->match =3D comp; + return 1; + } + + return 0; +} + +struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwn= ode) +{ + struct rvtrace_fwnode_match_data d =3D { .fwnode =3D fwnode, .match =3D N= ULL }; + int ret; + + ret =3D bus_for_each_dev(&rvtrace_bustype, NULL, &d, rvtrace_match_fwnode= ); + if (ret < 0) + return ERR_PTR(ret); + + return d.match; +} +EXPORT_SYMBOL_GPL(rvtrace_find_by_fwnode); + +int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset, + int bit, int bitval, int timeout) +{ + int i =3D 10; + u32 val; + + while (i--) { + val =3D rvtrace_read32(pdata, offset); + if (((val >> bit) & 0x1) =3D=3D bitval) + break; + udelay(timeout); + } + + return (i < 0) ? -ETIMEDOUT : 0; +} +EXPORT_SYMBOL_GPL(rvtrace_poll_bit); + +int rvtrace_enable_component(struct rvtrace_component *comp) +{ + u32 val; + + val =3D rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET); + val |=3D BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 1, + comp->pdata->control_poll_timeout_usecs); +} +EXPORT_SYMBOL_GPL(rvtrace_enable_component); + +int rvtrace_disable_component(struct rvtrace_component *comp) +{ + u32 val; + + val =3D rvtrace_read32(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET); + val &=3D ~BIT(RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT); + rvtrace_write32(comp->pdata, val, RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(comp->pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ENABLE_SHIFT, 0, + comp->pdata->control_poll_timeout_usecs); +} +EXPORT_SYMBOL_GPL(rvtrace_disable_component); + +struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu) +{ + if (!cpu_present(cpu)) + return NULL; + + return per_cpu(rvtrace_cpu_encoder, cpu); +} +EXPORT_SYMBOL_GPL(rvtrace_cpu_source); + +static int rvtrace_cleanup_inconn(struct device *dev, void *data) +{ + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + struct rvtrace_platform_data *pdata =3D comp->pdata; + struct rvtrace_connection *conn =3D data; + int i; + + if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) { + for (i =3D 0; i < pdata->nr_inconns; i++) { + if (pdata->inconns[i] !=3D conn) + continue; + pdata->inconns[i] =3D NULL; + return 1; + } + } + + return 0; +} + +static void rvtrace_cleanup_inconns_from_outconns(struct rvtrace_component= *comp) +{ + struct rvtrace_platform_data *pdata =3D comp->pdata; + struct rvtrace_connection *conn; + int i; + + lockdep_assert_held(&rvtrace_mutex); + + for (i =3D 0; i < pdata->nr_outconns; i++) { + conn =3D pdata->outconns[i]; + bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_cleanup_inconn); + } +} + +static int rvtrace_setup_inconn(struct device *dev, void *data) +{ + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + struct rvtrace_platform_data *pdata =3D comp->pdata; + struct rvtrace_connection *conn =3D data; + int i; + + if (device_match_fwnode(&comp->dev, conn->dest_fwnode)) { + for (i =3D 0; i < pdata->nr_inconns; i++) { + if (pdata->inconns[i]) + continue; + pdata->inconns[i] =3D conn; + return 1; + } + } + + return 0; +} + +static int rvtrace_setup_inconns_from_outconns(struct rvtrace_component *c= omp) +{ + struct rvtrace_platform_data *pdata =3D comp->pdata; + struct rvtrace_connection *conn; + int i, ret; + + lockdep_assert_held(&rvtrace_mutex); + + for (i =3D 0; i < pdata->nr_outconns; i++) { + conn =3D pdata->outconns[i]; + ret =3D bus_for_each_dev(&rvtrace_bustype, NULL, conn, rvtrace_setup_inc= onn); + if (ret < 0) { + rvtrace_cleanup_inconns_from_outconns(comp); + return ret; + } + } + + return 0; +} + +static void rvtrace_component_release(struct device *dev) +{ + struct rvtrace_component *comp =3D to_rvtrace_component(dev); + + fwnode_handle_put(comp->dev.fwnode); + rvtrace_free_type_idx(comp); + kfree(comp); +} + +static int rvtrace_component_reset(struct rvtrace_platform_data *pdata) +{ + int ret; + + rvtrace_write32(pdata, 0, RVTRACE_COMPONENT_CTRL_OFFSET); + ret =3D rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 0, + pdata->control_poll_timeout_usecs); + if (ret) + return ret; + + rvtrace_write32(pdata, RVTRACE_COMPONENT_CTRL_ACTIVE_MASK, + RVTRACE_COMPONENT_CTRL_OFFSET); + return rvtrace_poll_bit(pdata, RVTRACE_COMPONENT_CTRL_OFFSET, + RVTRACE_COMPONENT_CTRL_ACTIVE_SHIFT, 1, + pdata->control_poll_timeout_usecs); +} + +struct rvtrace_component *rvtrace_register_component(struct rvtrace_platfo= rm_data *pdata) +{ + struct rvtrace_connection *conn; + struct rvtrace_component *comp; + u32 impl, type, major, minor; + int i, ret =3D 0; + + if (!pdata || !pdata->dev) { + ret =3D -EINVAL; + goto err_out; + } + + for (i =3D 0; i < pdata->nr_inconns; i++) { + if (pdata->inconns[i]) { + ret =3D -EINVAL; + goto err_out; + } + } + + for (i =3D 0; i < pdata->nr_outconns; i++) { + conn =3D 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 =3D -EINVAL; + goto err_out; + } + } + + ret =3D rvtrace_component_reset(pdata); + if (ret) + goto err_out; + + impl =3D rvtrace_read32(pdata, RVTRACE_COMPONENT_IMPL_OFFSET); + type =3D (impl >> RVTRACE_COMPONENT_IMPL_TYPE_SHIFT) & + RVTRACE_COMPONENT_IMPL_TYPE_MASK; + major =3D (impl >> RVTRACE_COMPONENT_IMPL_VERMAJOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMAJOR_MASK; + minor =3D (impl >> RVTRACE_COMPONENT_IMPL_VERMINOR_SHIFT) & + RVTRACE_COMPONENT_IMPL_VERMINOR_MASK; + + if (pdata->bound_cpu >=3D 0 && !cpu_present(pdata->bound_cpu)) { + ret =3D -EINVAL; + goto err_out; + } + if (type =3D=3D RVTRACE_COMPONENT_TYPE_ENCODER && pdata->bound_cpu < 0) { + ret =3D -EINVAL; + goto err_out; + } + + comp =3D kzalloc(sizeof(*comp), GFP_KERNEL); + if (!comp) { + ret =3D -ENOMEM; + goto err_out; + } + comp->pdata =3D pdata; + comp->id.type =3D type; + comp->id.version =3D rvtrace_component_mkversion(major, minor); + ret =3D rvtrace_alloc_type_idx(comp); + if (ret) { + kfree(comp); + goto err_out; + } + + comp->dev.parent =3D pdata->dev; + comp->dev.coherent_dma_mask =3D pdata->dev->coherent_dma_mask; + comp->dev.release =3D rvtrace_component_release; + comp->dev.bus =3D &rvtrace_bustype; + comp->dev.fwnode =3D fwnode_handle_get(dev_fwnode(pdata->dev)); + switch (comp->id.type) { + case RVTRACE_COMPONENT_TYPE_ENCODER: + dev_set_name(&comp->dev, "encoder-%d", comp->type_idx); + break; + case RVTRACE_COMPONENT_TYPE_FUNNEL: + dev_set_name(&comp->dev, "funnel-%d", comp->type_idx); + break; + case RVTRACE_COMPONENT_TYPE_RAMSINK: + dev_set_name(&comp->dev, "ramsink-%d", comp->type_idx); + break; + case RVTRACE_COMPONENT_TYPE_PIBSINK: + dev_set_name(&comp->dev, "pibsink-%d", comp->type_idx); + break; + case RVTRACE_COMPONENT_TYPE_ATBBRIDGE: + dev_set_name(&comp->dev, "atbbridge-%d", comp->type_idx); + break; + default: + dev_set_name(&comp->dev, "type%d-%d", comp->id.type, comp->type_idx); + break; + } + + mutex_lock(&rvtrace_mutex); + + ret =3D device_register(&comp->dev); + if (ret) { + put_device(&comp->dev); + goto err_out_unlock; + } + + for (i =3D 0; i < pdata->nr_outconns; i++) { + conn =3D pdata->outconns[i]; + conn->src_comp =3D comp; + } + + ret =3D rvtrace_setup_inconns_from_outconns(comp); + if (ret < 0) { + device_unregister(&comp->dev); + goto err_out_unlock; + } + + if (comp->id.type =3D=3D RVTRACE_COMPONENT_TYPE_ENCODER) { + rvtrace_get_component(comp); + per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) =3D comp; + } + + mutex_unlock(&rvtrace_mutex); + + return comp; + +err_out_unlock: + mutex_unlock(&rvtrace_mutex); +err_out: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rvtrace_register_component); + +void rvtrace_unregister_component(struct rvtrace_component *comp) +{ + struct rvtrace_component *c; + + mutex_lock(&rvtrace_mutex); + + if (comp->id.type =3D=3D RVTRACE_COMPONENT_TYPE_ENCODER) { + c =3D per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu); + per_cpu(rvtrace_cpu_encoder, comp->pdata->bound_cpu) =3D NULL; + rvtrace_put_component(c); + } + + rvtrace_cleanup_inconns_from_outconns(comp); + device_unregister(&comp->dev); + + mutex_unlock(&rvtrace_mutex); +} +EXPORT_SYMBOL_GPL(rvtrace_unregister_component); + +int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver = *rtdrv) +{ + rtdrv->driver.owner =3D owner; + rtdrv->driver.bus =3D &rvtrace_bustype; + + return driver_register(&rtdrv->driver); +} +EXPORT_SYMBOL_GPL(__rvtrace_register_driver); + +static int __init rvtrace_init(void) +{ + int ret; + + rvtrace_init_type_idx(); + + ret =3D bus_register(&rvtrace_bustype); + if (ret) + return ret; + + ret =3D platform_driver_register(&rvtrace_platform_driver); + if (ret) { + bus_unregister(&rvtrace_bustype); + return ret; + } + + return 0; +} + +static void __exit rvtrace_exit(void) +{ + platform_driver_unregister(&rvtrace_platform_driver); + bus_unregister(&rvtrace_bustype); +} + +module_init(rvtrace_init); +module_exit(rvtrace_exit); diff --git a/drivers/hwtracing/rvtrace/rvtrace-platform.c b/drivers/hwtraci= ng/rvtrace/rvtrace-platform.c new file mode 100644 index 000000000000..a110ff1f2f08 --- /dev/null +++ b/drivers/hwtracing/rvtrace/rvtrace-platform.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int rvtrace_of_parse_outconns(struct rvtrace_platform_data *pdata) +{ + struct device_node *parent, *ep_node, *rep_node, *rdev_node; + struct rvtrace_connection *conn; + struct of_endpoint ep, rep; + int ret =3D 0, i =3D 0; + + parent =3D of_get_child_by_name(dev_of_node(pdata->dev), "out-ports"); + if (!parent) + return 0; + + pdata->nr_outconns =3D of_graph_get_endpoint_count(parent); + pdata->outconns =3D devm_kcalloc(pdata->dev, pdata->nr_outconns, + sizeof(*pdata->outconns), GFP_KERNEL); + if (!pdata->outconns) { + ret =3D -ENOMEM; + goto done; + } + + for_each_endpoint_of_node(parent, ep_node) { + conn =3D devm_kzalloc(pdata->dev, sizeof(*conn), GFP_KERNEL); + if (!conn) { + of_node_put(ep_node); + ret =3D -ENOMEM; + break; + } + + ret =3D of_graph_parse_endpoint(ep_node, &ep); + if (ret) { + of_node_put(ep_node); + break; + } + + rep_node =3D of_graph_get_remote_endpoint(ep_node); + if (!rep_node) { + ret =3D -ENODEV; + of_node_put(ep_node); + break; + } + rdev_node =3D of_graph_get_port_parent(rep_node); + + ret =3D of_graph_parse_endpoint(rep_node, &rep); + if (ret) { + of_node_put(ep_node); + break; + } + + conn->src_port =3D ep.port; + conn->src_fwnode =3D dev_fwnode(pdata->dev); + /* The 'src_comp' is set by rvtrace_register_component() */ + conn->src_comp =3D NULL; + conn->dest_port =3D rep.port; + conn->dest_fwnode =3D of_fwnode_handle(rdev_node); + conn->dest_comp =3D rvtrace_find_by_fwnode(conn->dest_fwnode); + if (!conn->dest_comp) { + ret =3D -EPROBE_DEFER; + of_node_put(ep_node); + } + + pdata->outconns[i] =3D conn; + i++; + } + +done: + of_node_put(parent); + return ret; +} + +static int rvtrace_of_parse_inconns(struct rvtrace_platform_data *pdata) +{ + struct device_node *parent; + int ret =3D 0; + + parent =3D of_get_child_by_name(dev_of_node(pdata->dev), "in-ports"); + if (!parent) + return 0; + + pdata->nr_inconns =3D of_graph_get_endpoint_count(parent); + pdata->inconns =3D devm_kcalloc(pdata->dev, pdata->nr_inconns, + sizeof(*pdata->inconns), GFP_KERNEL); + if (!pdata->inconns) + ret =3D -ENOMEM; + + of_node_put(parent); + return ret; +} + +static int rvtrace_platform_probe(struct platform_device *pdev) +{ + struct rvtrace_platform_data *pdata; + struct device *dev =3D &pdev->dev; + struct rvtrace_component *comp; + struct device_node *node; + struct resource *res; + int ret; + + pdata =3D devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + pdata->dev =3D dev; + pdata->impid =3D RVTRACE_COMPONENT_IMPID_UNKNOWN; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + pdata->io_mem =3D true; + pdata->base =3D 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 =3D -1; + node =3D of_parse_phandle(dev_of_node(dev), "cpu", 0); + if (node) { + ret =3D 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 =3D ret; + } + + /* Default control poll timeout */ + pdata->control_poll_timeout_usecs =3D 10; + + ret =3D rvtrace_of_parse_outconns(pdata); + if (ret) + return dev_err_probe(dev, ret, "failed to parse output connections\n"); + + ret =3D rvtrace_of_parse_inconns(pdata); + if (ret) + return dev_err_probe(dev, ret, "failed to parse input connections\n"); + + comp =3D rvtrace_register_component(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 rvtrace_component *comp =3D platform_get_drvdata(pdev); + + rvtrace_unregister_component(comp); +} + +static const struct of_device_id rvtrace_platform_match[] =3D { + { .compatible =3D "riscv,trace-component" }, + {} +}; + +struct platform_driver rvtrace_platform_driver =3D { + .driver =3D { + .name =3D "rvtrace", + .of_match_table =3D rvtrace_platform_match, + }, + .probe =3D rvtrace_platform_probe, + .remove =3D rvtrace_platform_remove, +}; diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h new file mode 100644 index 000000000000..04eb03e62601 --- /dev/null +++ b/include/linux/rvtrace.h @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Ventana Micro Systems Inc. + */ + +#ifndef __LINUX_RVTRACE_H__ +#define __LINUX_RVTRACE_H__ + +#include +#include +#include +#include +#include + +/* 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 + +/* 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 + +/* Possible 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 +}; + +/* Encoding/decoding macros for RISC-V trace component version */ +#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)) + +/* + * 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__ + */ +enum rvtrace_component_impid { + RVTRACE_COMPONENT_IMPID_UNKNOWN, + RVTRACE_COMPONENT_IMPID_MAX +}; + +/** + * struct rvtrace_connection - Representation of a physical connection bet= ween + * two RISC-V 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 rvtrace_connection { + int src_port; + struct fwnode_handle *src_fwnode; + int dest_port; + struct fwnode_handle *dest_fwnode; + struct rvtrace_component *src_comp; + struct rvtrace_component *dest_comp; +}; + +/** + * struct rvtrace_platform_data - Platform-level data for a RISC-V trace c= omponent + * discovered from DT or ACPI. + * @dev: Parent device. + * @impid: Component implementation ID + * @io_mem: Flag showing whether component registers are memory mappe= d. + * @base: If io_mem =3D=3D true then base address of the memory map= ped registers. + * @read: If io_mem =3D=3D false then read register from the given = "offset". + * @write: If io_mem =3D=3D false then write register to the given "= offset". + * @bound_cpu: CPU to which the component is bound. This should be -1 if + * the component is not bound to any CPU. For encoder compon= ent + * type this must not be -1. + * @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 rvtrace_platform_data { + struct device *dev; + + enum rvtrace_component_impid impid; + + bool io_mem; + union { + void __iomem *base; + struct { + u32 (*read)(struct rvtrace_platform_data *pdata, + u32 offset, bool relaxed); + void (*write)(struct rvtrace_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 rvtrace_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 rvtrace_connection **outconns; +}; + +static inline u32 rvtrace_read32(struct rvtrace_platform_data *pdata, u32 = offset) +{ + if (likely(pdata->io_mem)) + return readl(pdata->base + offset); + + return pdata->read(pdata, offset, false); +} + +static inline u32 rvtrace_relaxed_read32(struct rvtrace_platform_data *pda= ta, u32 offset) +{ + if (likely(pdata->io_mem)) + return readl_relaxed(pdata->base + offset); + + return pdata->read(pdata, offset, true); +} + +static inline void rvtrace_write32(struct rvtrace_platform_data *pdata, u3= 2 val, u32 offset) +{ + if (likely(pdata->io_mem)) + writel(val, pdata->base + offset); + else + pdata->write(pdata, val, offset, false); +} + +static inline void rvtrace_relaxed_write32(struct rvtrace_platform_data *p= data, + 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 rvtrace_is_source(struct rvtrace_platform_data *pdata) +{ + return !pdata->nr_inconns ? true : false; +} + +static inline bool rvtrace_is_sink(struct rvtrace_platform_data *pdata) +{ + return !pdata->nr_outconns ? true : false; +} + +/** + * struct rvtrace_component_id - Details to identify or match a RISC-V tra= ce component + * @type: Type of the component + * @version: Version of the component + * @data: Data pointer for driver use + */ +struct rvtrace_component_id { + enum rvtrace_component_type type; + u32 version; + void *data; +}; + +/** + * struct rvtrace_component - Representation of a RISC-V 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 RISC-V trace driver was probed successfu= lly + */ +struct rvtrace_component { + struct rvtrace_platform_data *pdata; + struct rvtrace_component_id id; + u32 type_idx; + struct device dev; + bool ready; +}; + +#define to_rvtrace_component(__dev) container_of_const(__dev, struct rvtra= ce_component, dev) + +static inline void rvtrace_get_component(struct rvtrace_component *comp) +{ + get_device(&comp->dev); +} + +static inline void rvtrace_put_component(struct rvtrace_component *comp) +{ + put_device(&comp->dev); +} + +const struct rvtrace_component_id *rvtrace_match_id(struct rvtrace_compone= nt *comp, + const struct rvtrace_component_id *ids); +struct rvtrace_component *rvtrace_find_by_fwnode(struct fwnode_handle *fwn= ode); + +int rvtrace_poll_bit(struct rvtrace_platform_data *pdata, int offset, + int bit, int bitval, int timeout); +int rvtrace_enable_component(struct rvtrace_component *comp); +int rvtrace_disable_component(struct rvtrace_component *comp); + +struct rvtrace_component *rvtrace_cpu_source(unsigned int cpu); + +struct rvtrace_component *rvtrace_register_component(struct rvtrace_platfo= rm_data *pdata); +void rvtrace_unregister_component(struct rvtrace_component *comp); + +/** + * struct rvtrace_driver - Representation of a RISC-V trace driver + * id_table: Table to match components handled by the driver + * probe: Driver probe() function + * remove: Driver remove() function + * driver: Device driver instance + */ +struct rvtrace_driver { + const struct rvtrace_component_id *id_table; + int (*probe)(struct rvtrace_component *comp); + void (*remove)(struct rvtrace_component *comp); + struct device_driver driver; +}; + +#define to_rvtrace_driver(__drv) \ + ((__drv) ? container_of_const((__drv), struct rvtrace_driver, driver) : N= ULL) + +extern struct platform_driver rvtrace_platform_driver; + +int __rvtrace_register_driver(struct module *owner, struct rvtrace_driver = *rtdrv); +#define rvtrace_register_driver(driver) __rvtrace_register_driver(THIS_MOD= ULE, driver) +static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv) +{ + if (rtdrv) + driver_unregister(&rtdrv->driver); +} + +#endif --=20 2.43.0