From nobody Mon Oct 6 04:57:12 2025 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60EAB2EB5C0 for ; Fri, 25 Jul 2025 10:08:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753438131; cv=none; b=Xhfjn9sPIrXxsfQqKBBlLZWCr6OXMtP7u21Z5qkEJ9RgVPVudqiljLZq61Wl4X2KBba/yHiM+W+vxXc2/To7STI7N7QDKPDHws4Hd9HJ6ikdwwNb3Da//sCIEKvM/j4tYWRJmqVnRIKZy6MRc6yDLsNsHyx2ovZIUL3C3rLEJOs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753438131; c=relaxed/simple; bh=HCB1vNSzb6glzOd5Yewe2kx+lbfK3zHoM8hyj/k+mWU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=eKHGb+9jxSgPpD41tI0VIYkbuNIwz02ImTgfv2ZiMEI6dX7KsBvCPECW3+qlMP8VYLhrAqSxATd0INt4uywULAJbWuCiMT2h2JkXzR7wFquzrtGa77oYKVj2q7tDZnCSN2A8xVSsrq8OMgG7IzA5LwZPr3diST7CNVvmJqbWy/4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=LWT8vi1C; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="LWT8vi1C" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 56P8r78K015756 for ; Fri, 25 Jul 2025 10:08:48 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=NdA3mUaEV7+ 9wutVGVbJGrP5eFfiagtLlYrTJRTE/g4=; b=LWT8vi1CVGDyfxht4Wlvib0OuUk dY8bakLas9dOmtbThxb4jYyi3YIjRwvS4k7RxklBrCaPQfIjAh8+wjNIoBzcYt01 EaAxMNHngkbAa19xm/dld49wBygq3oVWm3V3J/lPeLEVqJ+Okah4p/lV8+z4ftnH UX8+8qpx3IbQ7ouL4fr/N1yjV6I/D3raPRQitFq+nuCHlb8L7aK3NEbVVFS3J0M4 rG8ifWXX4kD2oggCLwhVUZSQFfBR+3hgbalIxZd8N7AEFBdJukPjSR2TVkGEhAo4 YS5WgDPR5y5RxF4dWr6Lr0pXQup1VTg3TJHFIw3h/ivCL6LcxkwhAs7LkCg== Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 483w501nq3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 25 Jul 2025 10:08:47 +0000 (GMT) Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-23536f7c2d7so33633235ad.2 for ; Fri, 25 Jul 2025 03:08:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753438126; x=1754042926; 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=NdA3mUaEV7+9wutVGVbJGrP5eFfiagtLlYrTJRTE/g4=; b=nVNQ9SYXC/6kVFGpFz2yO6TrSg4j1dunlzQiRe2HY2TxqDlKzlPQDvO9H2189i2Sud vSKC3dH5id0PTn3un0qjicP6JOWix//xfH2/Qikyh+zuKbdYkGoSb7qdiK9aoNG6dno9 ZFkYgiXvML/V0oAOhXiwiSaWj3FbeIgkVPYWJ6gobZb94nW+eoEA9PD1ZBxr0Ec9T92M dtVDcddH0IW50VOBP5EURZW4S5dKbrOhUlNUpHx4sTxrCiQKVz1mk26IV300xXYSPVba pcluVDtdmZGvxd/hErEA9ZxTg+mWkI/78fYR0mixSC45+q6iMqour2mY60hkdtEaVe7T 8utA== X-Forwarded-Encrypted: i=1; AJvYcCVcQgElWgF75a9x2paOwQ3ZemJFtQiaVcEhTbx4iXlxqHFaaYzRugmr1qZWn82xUgDvN1BoYERvydN50lk=@vger.kernel.org X-Gm-Message-State: AOJu0Yz7y6ZVNNzes5yX/NUbInIvvGhdRquRixbtggl4abmUyz2QBQZL 88Pw5fwXJfT7kfk3deNDzEYhvoB/4xQgjIbmGPAqyUEUhz8ApuZPcRHmd7P1yYI5sufGBq2Hi3x DE7T2kXGEuR8tz0jjpp79kewsNwZ7CuRRh2U+cJE3J4l6rLAvtOpVPjiq14mJBvaTx5I= X-Gm-Gg: ASbGncv8xWJiJKwmI3o8ZEXsLkRsZdPQz4LPVn8o4Hb1z8O63TDSJgJeaUecYnfHjQd oayIONi8AdOqoYOwyZccMge6mX8eoOfvKk+zr/2X960IBxyCDvenlYlMy4bG/RmnF0U5vnCtR9g 1RBClxelV4OuBiSqJVzKnYWTSvvvQiJ9SJs+X2EwTjW4CwRKMSkZcNmoUvW1wRLKEdP11y7HJHl hAkmHPYAvRffuAtfthM47CvLDy52ZQYgzzyFSV6awBE9gPekSxo0x+zOO4SdMG6aNkPq9/Tg0+R 6Xrw6IqpAyZHmJTt8pewheMiTQufIQBS8hw+dl5953/pVPFjMOuSviBeD9iBRyilT/TPIJ2cOfe EUfzxtW2mxW6iV/4DWmk= X-Received: by 2002:a17:902:e78b:b0:223:619e:71da with SMTP id d9443c01a7336-23fb31e3307mr23987765ad.49.1753438125833; Fri, 25 Jul 2025 03:08:45 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFMt/+402zHmDoTmYIzCMlaQRe4GBHEO6JqEZC+bbr5hdDCV1yBalQp3LzcY43Xi+qjTEtf8Q== X-Received: by 2002:a17:902:e78b:b0:223:619e:71da with SMTP id d9443c01a7336-23fb31e3307mr23987215ad.49.1753438125280; Fri, 25 Jul 2025 03:08:45 -0700 (PDT) Received: from jiegan.qualcomm.com (tpe-colo-wan-fw-bordernet.qualcomm.com. [103.229.16.4]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-23fa48fd29dsm33641435ad.176.2025.07.25.03.08.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Jul 2025 03:08:44 -0700 (PDT) From: Jie Gan To: Suzuki K Poulose , Mike Leach , James Clark , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , Alexander Shishkin , Tingwei Zhang , Jinlong Mao Cc: coresight@lists.linaro.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, Jie Gan Subject: [PATCH v4 07/10] coresight: ctcu: enable byte-cntr for TMC ETR devices Date: Fri, 25 Jul 2025 18:08:03 +0800 Message-Id: <20250725100806.1157-8-jie.gan@oss.qualcomm.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250725100806.1157-1-jie.gan@oss.qualcomm.com> References: <20250725100806.1157-1-jie.gan@oss.qualcomm.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 X-Proofpoint-GUID: lA7XxAua-4E6_Kr7ge6QjjshERyYbKJ3 X-Proofpoint-ORIG-GUID: lA7XxAua-4E6_Kr7ge6QjjshERyYbKJ3 X-Authority-Analysis: v=2.4 cv=bKAWIO+Z c=1 sm=1 tr=0 ts=688357af cx=c_pps a=MTSHoo12Qbhz2p7MsH1ifg==:117 a=nuhDOHQX5FNHPW3J6Bj6AA==:17 a=Wb1JkmetP80A:10 a=EUspDBNiAAAA:8 a=TrPPj8Ph-2G9coJyck8A:9 a=GvdueXVYPmCkWapjIL-Q:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNzI1MDA4NSBTYWx0ZWRfX09liLlyr2KDk 9HI8829Gcrfl7rueFzZj9xDb8a7mT4fF5MxXuhcwCTquENfYDOv4bV9k/fvRUCsAij2bZ7OboDL tSdcVgsq20YZPx5ZS064yznTwk+4WzfjunPJ2qhQVfJqa2Al/trkf0LH/w7PslcPAU7EaBF91Jr tgXpdVrM0O6aK2p+RVU/im8SHkBc3PjteSM157MJ4p88W2mmc2bP5+sJmemLvqhylCVFB9NLTDA 3nCs6vUvPmoA6mzwbQd4dozaPSebL3+mYQ2984HarAMgXHul6JilgRjpHoBdIZ23aBLwzppK7ws v3iDsKVoo0mD7ZQYczXUPctuWuBWMKhqucDKWOZnpguuo4u2FV2h+kTQAdJA2p9bJyh/1nAFXYb E1/kgSRr6qhyywXSNnFqNcVbIMiJrBAhVQnZO3vBtiPnxKpk/HoISmOgfD4ol7MNIQuFLqWV X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.1.9,FMLib:17.12.80.40 definitions=2025-07-25_02,2025-07-24_01,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 spamscore=0 mlxlogscore=999 clxscore=1015 phishscore=0 priorityscore=1501 lowpriorityscore=0 adultscore=0 mlxscore=0 suspectscore=0 malwarescore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2507250085 Content-Type: text/plain; charset="utf-8" The byte-cntr function provided by the CTCU device is used to transfer data from the ETR buffer to the userspace. An interrupt is triggered if the data size exceeds the threshold set in the BYTECNTRVAL register. The interrupt handler counts the number of triggered interruptions and the read function will read the data from the synced ETR buffer. Switching the sysfs_buf when current buffer is full or the timeout is triggered and resets rrp and rwp registers after switched the buffer. The synced buffer will become available for reading after the switch. Signed-off-by: Jie Gan --- .../testing/sysfs-bus-coresight-devices-ctcu | 5 + drivers/hwtracing/coresight/Makefile | 2 +- .../coresight/coresight-ctcu-byte-cntr.c | 364 ++++++++++++++++++ .../hwtracing/coresight/coresight-ctcu-core.c | 94 ++++- drivers/hwtracing/coresight/coresight-ctcu.h | 60 ++- .../hwtracing/coresight/coresight-tmc-etr.c | 16 + drivers/hwtracing/coresight/coresight-tmc.h | 2 + 7 files changed, 530 insertions(+), 13 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-c= tcu create mode 100644 drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu b/D= ocumentation/ABI/testing/sysfs-bus-coresight-devices-ctcu new file mode 100644 index 000000000000..43064bf1aac7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu @@ -0,0 +1,5 @@ +What: /sys/bus/coresight/devices//irq_val +Date: June 2025 +KernelVersion: 6.16 +Contact: Tingwei Zhang ; Jinlong Ma= o ; Jie Gan +Description: (RW) Configure the IRQ value for byte-cntr register. diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/cores= ight/Makefile index ab16d06783a5..821a1b06b20c 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -55,5 +55,5 @@ coresight-cti-y :=3D coresight-cti-core.o coresight-cti-p= latform.o \ obj-$(CONFIG_ULTRASOC_SMB) +=3D ultrasoc-smb.o obj-$(CONFIG_CORESIGHT_DUMMY) +=3D coresight-dummy.o obj-$(CONFIG_CORESIGHT_CTCU) +=3D coresight-ctcu.o -coresight-ctcu-y :=3D coresight-ctcu-core.o +coresight-ctcu-y :=3D coresight-ctcu-core.o coresight-ctcu-byte-cntr.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) +=3D coresight-kunit-tests.o diff --git a/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c b/drive= rs/hwtracing/coresight/coresight-ctcu-byte-cntr.c new file mode 100644 index 000000000000..83e4a17d897f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "coresight-ctcu.h" +#include "coresight-priv.h" +#include "coresight-tmc.h" + +static irqreturn_t byte_cntr_handler(int irq, void *data) +{ + struct ctcu_byte_cntr *byte_cntr_data =3D (struct ctcu_byte_cntr *)data; + + atomic_inc(&byte_cntr_data->irq_cnt); + wake_up(&byte_cntr_data->wq); + + return IRQ_HANDLED; +} + +/* Start the byte-cntr function when the path is enabled. */ +void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight= _path *path) +{ + struct ctcu_drvdata *drvdata =3D dev_get_drvdata(csdev->dev.parent); + struct coresight_device *sink =3D coresight_get_sink(path); + struct ctcu_byte_cntr *byte_cntr_data; + int port_num; + + if (!sink) + return; + + port_num =3D coresight_get_in_port_dest(sink, csdev); + if (port_num < 0) + return; + + byte_cntr_data =3D &drvdata->byte_cntr_data[port_num]; + /* Don't start byte-cntr function when threshold is not set. */ + if (!byte_cntr_data->thresh_val || byte_cntr_data->enable) + return; + + guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock); + byte_cntr_data->enable =3D true; + byte_cntr_data->reading_buf =3D false; +} + +/* Stop the byte-cntr function when the path is disabled. */ +void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_= path *path) +{ + struct ctcu_drvdata *drvdata =3D dev_get_drvdata(csdev->dev.parent); + struct coresight_device *sink =3D coresight_get_sink(path); + struct ctcu_byte_cntr *byte_cntr_data; + int port_num; + + if (!sink || coresight_get_mode(sink) =3D=3D CS_MODE_SYSFS) + return; + + port_num =3D coresight_get_in_port_dest(sink, csdev); + if (port_num < 0) + return; + + byte_cntr_data =3D &drvdata->byte_cntr_data[port_num]; + guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock); + byte_cntr_data->enable =3D false; +} + +static void ctcu_reset_sysfs_buf(struct tmc_drvdata *drvdata) +{ + u32 sts; + + CS_UNLOCK(drvdata->base); + tmc_write_rrp(drvdata, drvdata->sysfs_buf->hwaddr); + tmc_write_rwp(drvdata, drvdata->sysfs_buf->hwaddr); + sts =3D readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; + writel_relaxed(sts, drvdata->base + TMC_STS); + CS_LOCK(drvdata->base); +} + +static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct tmc_drvdata *= drvdata) +{ + struct ctcu_byte_cntr *byte_cntr_data; + struct ctcu_drvdata *ctcu_drvdata; + struct coresight_device *helper; + int port; + + helper =3D coresight_get_helper(drvdata->csdev, CORESIGHT_DEV_SUBTYPE_HEL= PER_CTCU); + if (!helper) + return NULL; + + port =3D coresight_get_in_port_dest(drvdata->csdev, helper); + if (port < 0) + return NULL; + + ctcu_drvdata =3D dev_get_drvdata(helper->dev.parent); + byte_cntr_data =3D &ctcu_drvdata->byte_cntr_data[port]; + return byte_cntr_data; +} + +static bool ctcu_byte_cntr_switch_buffer(struct tmc_drvdata *drvdata, + struct ctcu_byte_cntr *byte_cntr_data) +{ + struct etr_buf_node *nd, *next, *curr_node, *picked_node; + struct etr_buf *curr_buf =3D drvdata->sysfs_buf; + bool found_free_buf =3D false; + + if (WARN_ON(!drvdata || !byte_cntr_data)) + return found_free_buf; + + /* Stop the ETR before we start the switch */ + if (coresight_get_mode(drvdata->csdev) !=3D CS_MODE_DISABLED) + tmc_etr_disable_hw_before_switching(drvdata); + + list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) { + /* curr_buf is free for next round */ + if (nd->sysfs_buf =3D=3D curr_buf) { + nd->is_free =3D true; + curr_node =3D nd; + } + + if (!found_free_buf && nd->is_free && nd->sysfs_buf !=3D curr_buf) { + if (nd->reading) + continue; + + picked_node =3D nd; + found_free_buf =3D true; + } + } + + if (found_free_buf) { + curr_node->reading =3D true; + curr_node->pos =3D 0; + drvdata->reading_node =3D curr_node; + drvdata->sysfs_buf =3D picked_node->sysfs_buf; + drvdata->etr_buf =3D picked_node->sysfs_buf; + picked_node->is_free =3D false; + /* Reset irq_cnt for next etr_buf */ + atomic_set(&byte_cntr_data->irq_cnt, 0); + /* Reset rrp and rwp when the system has switched the buffer*/ + ctcu_reset_sysfs_buf(drvdata); + /* Restart the ETR when we find a free buffer */ + if (coresight_get_mode(drvdata->csdev) !=3D CS_MODE_DISABLED) + tmc_etr_enable_hw_after_switching(drvdata); + } + + return found_free_buf; +} + +/* + * ctcu_byte_cntr_get_data() - reads data from the deactivated and filled = buffer. + * The byte-cntr reading work reads data from the deactivated and filled b= uffer. + * The read operation waits for a buffer to become available, either fille= d or + * upon timeout, and then reads trace data from the synced buffer. + */ +static ssize_t ctcu_byte_cntr_get_data(struct tmc_drvdata *drvdata, loff_t= pos, + size_t len, char **bufpp) +{ + struct etr_buf *sysfs_buf =3D drvdata->sysfs_buf; + struct device *dev =3D &drvdata->csdev->dev; + ssize_t actual, size =3D sysfs_buf->size; + struct ctcu_byte_cntr *byte_cntr_data; + struct etr_buf_node *nd, *next; + size_t thresh_val; + atomic_t *irq_cnt; + int ret; + + byte_cntr_data =3D ctcu_get_byte_cntr_data(drvdata); + if (!byte_cntr_data) + return -EINVAL; + + thresh_val =3D byte_cntr_data->thresh_val; + irq_cnt =3D &byte_cntr_data->irq_cnt; + +wait_buffer: + if (!byte_cntr_data->reading_buf) { + ret =3D wait_event_interruptible_timeout(byte_cntr_data->wq, + ((atomic_read(irq_cnt) + 1) * thresh_val >=3D size) || + !byte_cntr_data->enable, + BYTE_CNTR_TIMEOUT); + if (ret < 0) + return ret; + /* + * The current etr_buf is almost full or timeout is triggered, + * so switch the buffer and mark the switched buffer as reading. + */ + if (byte_cntr_data->enable) { + if (!ctcu_byte_cntr_switch_buffer(drvdata, byte_cntr_data)) { + dev_err(dev, "Switch buffer failed for byte-cntr\n"); + return -EINVAL; + } + + byte_cntr_data->reading_buf =3D true; + } else { + if (!drvdata->reading_node) { + list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) { + if (nd->sysfs_buf =3D=3D sysfs_buf) { + nd->pos =3D 0; + drvdata->reading_node =3D nd; + break; + } + } + } + + pos =3D drvdata->reading_node->pos; + actual =3D drvdata->read_ops->get_trace_data(drvdata, pos, len, bufpp); + if (actual > 0) { + byte_cntr_data->total_size +=3D actual; + return actual; + } + + drvdata->reading_node =3D NULL; + + /* Exit byte-cntr reading */ + return -EINVAL; + } + } + + /* Check the status of current etr_buf*/ + if ((atomic_read(irq_cnt) + 1) * thresh_val >=3D size) + /* + * Unlikely to find a free buffer to switch, so just disable + * the ETR for a while. + */ + if (!ctcu_byte_cntr_switch_buffer(drvdata, byte_cntr_data)) + dev_info(dev, "No available buffer to store data, disable ETR\n"); + + pos =3D drvdata->reading_node->pos; + actual =3D drvdata->read_ops->get_trace_data(drvdata, pos, len, bufpp); + if (actual =3D=3D 0) { + /* Reading work for marked buffer has finished, reset flags */ + drvdata->reading_node->reading =3D false; + byte_cntr_data->reading_buf =3D false; + drvdata->reading_node =3D NULL; + + /* Nothing in the buffer, wait for next buffer to be filled */ + goto wait_buffer; + } + byte_cntr_data->total_size +=3D actual; + + return actual; +} + +static int ctcu_read_prepare_byte_cntr(struct tmc_drvdata *drvdata) +{ + struct ctcu_byte_cntr *byte_cntr_data; + unsigned long flags; + int ret =3D 0; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type !=3D TMC_CONFIG_TYPE_ETR)) + return -EINVAL; + + byte_cntr_data =3D ctcu_get_byte_cntr_data(drvdata); + if (!byte_cntr_data) + return -EINVAL; + + /* + * The threshold value must not exceed the buffer size. + * A margin should be maintained between the two values to account + * for the time gap between the interrupt and buffer switching. + */ + if (byte_cntr_data->thresh_val + SZ_16K >=3D drvdata->size) { + dev_err(&drvdata->csdev->dev, "The threshold value is too large\n"); + return -EINVAL; + } + + raw_spin_lock_irqsave(&drvdata->spinlock, flags); + if (byte_cntr_data->reading) { + ret =3D -EBUSY; + goto out_unlock; + } + + byte_cntr_data->reading =3D true; + raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); + /* Insert current sysfs_buf into the list */ + ret =3D tmc_create_etr_buf_node(drvdata, drvdata->sysfs_buf); + if (!ret) { + /* + * Add one more sysfs_buf for byte-cntr function, byte-cntr always reads + * the data from the buffer which has been synced. Switch the buffer when + * the used buffer is nearly full. The used buffer will be synced and ma= de + * available for reading before switch. + */ + ret =3D tmc_create_etr_buf_node(drvdata, NULL); + if (ret) { + dev_err(&drvdata->csdev->dev, "Failed to create etr_buf_node\n"); + tmc_clean_etr_buf_list(drvdata); + byte_cntr_data->reading =3D false; + goto out; + } + } + + raw_spin_lock_irqsave(&drvdata->spinlock, flags); + atomic_set(&byte_cntr_data->irq_cnt, 0); + enable_irq(byte_cntr_data->irq_num); + enable_irq_wake(byte_cntr_data->irq_num); + byte_cntr_data->total_size =3D 0; + +out_unlock: + raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); + +out: + return ret; +} + +static int ctcu_read_unprepare_byte_cntr(struct tmc_drvdata *drvdata) +{ + struct device *dev =3D &drvdata->csdev->dev; + struct ctcu_byte_cntr *byte_cntr_data; + + byte_cntr_data =3D ctcu_get_byte_cntr_data(drvdata); + if (!byte_cntr_data) + return -EINVAL; + + guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock); + disable_irq_wake(byte_cntr_data->irq_num); + disable_irq(byte_cntr_data->irq_num); + byte_cntr_data->reading =3D false; + tmc_clean_etr_buf_list(drvdata); + dev_dbg(dev, "send data total size:%llu bytes\n", byte_cntr_data->total_s= ize); + + return 0; +} + +static const struct tmc_read_ops byte_cntr_read_ops =3D { + .read_prepare =3D ctcu_read_prepare_byte_cntr, + .read_unprepare =3D ctcu_read_unprepare_byte_cntr, + .get_trace_data =3D ctcu_byte_cntr_get_data, +}; + +void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata,= int etr_num) +{ + struct ctcu_byte_cntr *byte_cntr_data; + struct device_node *nd =3D dev->of_node; + int irq_num, ret, i; + + drvdata->byte_cntr_read_ops =3D &byte_cntr_read_ops; + for (i =3D 0; i < etr_num; i++) { + byte_cntr_data =3D &drvdata->byte_cntr_data[i]; + irq_num =3D of_irq_get_byname(nd, byte_cntr_data->irq_name); + if (irq_num < 0) { + dev_err(dev, "Failed to get IRQ from DT for %s\n", + byte_cntr_data->irq_name); + continue; + } + + ret =3D devm_request_irq(dev, irq_num, byte_cntr_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, + dev_name(dev), byte_cntr_data); + if (ret) { + dev_err(dev, "Failed to register IRQ for %s\n", + byte_cntr_data->irq_name); + continue; + } + + byte_cntr_data->irq_num =3D irq_num; + disable_irq(byte_cntr_data->irq_num); + init_waitqueue_head(&byte_cntr_data->wq); + } +} diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hw= tracing/coresight/coresight-ctcu-core.c index 3bdedf041390..8fc08e42187e 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -15,6 +15,7 @@ #include #include #include +#include =20 #include "coresight-ctcu.h" #include "coresight-priv.h" @@ -45,17 +46,23 @@ DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu"); =20 #define CTCU_ATID_REG_BIT(traceid) (traceid % 32) #define CTCU_ATID_REG_SIZE 0x10 +#define CTCU_ETR0_IRQCTRL 0x6c +#define CTCU_ETR1_IRQCTRL 0x70 #define CTCU_ETR0_ATID0 0xf8 #define CTCU_ETR1_ATID0 0x108 =20 static const struct ctcu_etr_config sa8775p_etr_cfgs[] =3D { { - .atid_offset =3D CTCU_ETR0_ATID0, - .port_num =3D 0, + .atid_offset =3D CTCU_ETR0_ATID0, + .irq_ctrl_offset =3D CTCU_ETR0_IRQCTRL, + .irq_name =3D "etr0", + .port_num =3D 0, }, { - .atid_offset =3D CTCU_ETR1_ATID0, - .port_num =3D 1, + .atid_offset =3D CTCU_ETR1_ATID0, + .irq_ctrl_offset =3D CTCU_ETR1_IRQCTRL, + .irq_name =3D "etr1", + .port_num =3D 1, }, }; =20 @@ -64,6 +71,76 @@ static const struct ctcu_config sa8775p_cfgs =3D { .num_etr_config =3D ARRAY_SIZE(sa8775p_etr_cfgs), }; =20 +static void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u= 32 offset) +{ + CS_UNLOCK(drvdata->base); + ctcu_writel(drvdata, val, offset); + CS_LOCK(drvdata->base); +} + +static ssize_t irq_threshold_show(struct device *dev, struct device_attrib= ute *attr, + char *buf) +{ + struct ctcu_drvdata *drvdata =3D dev_get_drvdata(dev->parent); + int i, len =3D 0; + + for (i =3D 0; i < ETR_MAX_NUM; i++) { + if (drvdata->byte_cntr_data[i].irq_ctrl_offset) + len +=3D scnprintf(buf + len, PAGE_SIZE - len, "%u ", + drvdata->byte_cntr_data[i].thresh_val); + } + + len +=3D scnprintf(buf + len, PAGE_SIZE - len, "\n"); + + return len; +} + +/* Program a valid value into IRQCTRL register will enable byte-cntr inter= rupt */ +static ssize_t irq_threshold_store(struct device *dev, struct device_attri= bute *attr, + const char *buf, size_t size) +{ + struct ctcu_drvdata *drvdata =3D dev_get_drvdata(dev->parent); + u32 thresh_vals[ETR_MAX_NUM] =3D { 0 }; + u32 irq_ctrl_offset; + int num, i; + + num =3D sscanf(buf, "%i %i", &thresh_vals[0], &thresh_vals[1]); + if (num <=3D 0 || num > ETR_MAX_NUM) + return -EINVAL; + + /* Threshold 0 disables the interruption. */ + guard(raw_spinlock_irqsave)(&drvdata->spin_lock); + for (i =3D 0; i < num; i++) { + /* A small threshold will result in a large number of interruptions */ + if (thresh_vals[i] && thresh_vals[i] < SZ_4K) + return -EINVAL; + + if (drvdata->byte_cntr_data[i].irq_ctrl_offset) { + drvdata->byte_cntr_data[i].thresh_val =3D thresh_vals[i]; + irq_ctrl_offset =3D drvdata->byte_cntr_data[i].irq_ctrl_offset; + /* A one value for IRQCTRL register represents 8 bytes */ + ctcu_program_register(drvdata, thresh_vals[i] / 8, irq_ctrl_offset); + } + } + + return size; +} +static DEVICE_ATTR_RW(irq_threshold); + +static struct attribute *ctcu_attrs[] =3D { + &dev_attr_irq_threshold.attr, + NULL, +}; + +static struct attribute_group ctcu_attr_grp =3D { + .attrs =3D ctcu_attrs, +}; + +static const struct attribute_group *ctcu_attr_grps[] =3D { + &ctcu_attr_grp, + NULL, +}; + static void ctcu_program_atid_register(struct ctcu_drvdata *drvdata, u32 r= eg_offset, u8 bit, bool enable) { @@ -143,6 +220,8 @@ static int ctcu_enable(struct coresight_device *csdev, = enum cs_mode mode, void * { struct coresight_path *path =3D (struct coresight_path *)data; =20 + ctcu_byte_cntr_start(csdev, path); + return ctcu_set_etr_traceid(csdev, path, true); } =20 @@ -150,6 +229,8 @@ static int ctcu_disable(struct coresight_device *csdev,= void *data) { struct coresight_path *path =3D (struct coresight_path *)data; =20 + ctcu_byte_cntr_stop(csdev, path); + return ctcu_set_etr_traceid(csdev, path, false); } =20 @@ -200,7 +281,11 @@ static int ctcu_probe(struct platform_device *pdev) for (i =3D 0; i < cfgs->num_etr_config; i++) { etr_cfg =3D &cfgs->etr_cfgs[i]; drvdata->atid_offset[i] =3D etr_cfg->atid_offset; + drvdata->byte_cntr_data[i].irq_name =3D etr_cfg->irq_name; + drvdata->byte_cntr_data[i].irq_ctrl_offset =3D + etr_cfg->irq_ctrl_offset; } + ctcu_byte_cntr_init(dev, drvdata, cfgs->num_etr_config); } } =20 @@ -212,6 +297,7 @@ static int ctcu_probe(struct platform_device *pdev) desc.subtype.helper_subtype =3D CORESIGHT_DEV_SUBTYPE_HELPER_CTCU; desc.pdata =3D pdata; desc.dev =3D dev; + desc.groups =3D ctcu_attr_grps; desc.ops =3D &ctcu_ops; desc.access =3D CSDEV_ACCESS_IOMEM(base); =20 diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/hwtraci= ng/coresight/coresight-ctcu.h index e9594c38dd91..894e375277c4 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu.h +++ b/drivers/hwtracing/coresight/coresight-ctcu.h @@ -5,19 +5,28 @@ =20 #ifndef _CORESIGHT_CTCU_H #define _CORESIGHT_CTCU_H + +#include #include "coresight-trace-id.h" +#include "coresight-tmc.h" =20 /* Maximum number of supported ETR devices for a single CTCU. */ #define ETR_MAX_NUM 2 =20 +#define BYTE_CNTR_TIMEOUT (5 * HZ) + /** * struct ctcu_etr_config * @atid_offset: offset to the ATID0 Register. - * @port_num: in-port number of CTCU device that connected to ETR. + * @port_num: in-port number of the CTCU device that connected to ETR. + * @irq_ctrl_offset: offset to the BYTECNTRVAL register. + * @irq_name: IRQ name in dt node. */ struct ctcu_etr_config { const u32 atid_offset; const u32 port_num; + const u32 irq_ctrl_offset; + const char *irq_name; }; =20 struct ctcu_config { @@ -25,15 +34,50 @@ struct ctcu_config { int num_etr_config; }; =20 -struct ctcu_drvdata { - void __iomem *base; - struct clk *apb_clk; - struct device *dev; - struct coresight_device *csdev; +/** + * struct ctcu_byte_cntr + * @enable: indicates that byte_cntr function is enabled or not. + * @reading: indicates that byte-cntr reading is started. + * @reading_buf: indicates that byte-cntr is reading data from the buffer. + * @thresh_val: threshold to trigger a interruption. + * @total_size: total size of transferred data. + * @irq_num: allocated number of the IRQ. + * @irq_cnt: IRQ count number for triggered interruptions. + * @wq: waitqueue for reading data from ETR buffer. + * @spin_lock: spinlock of byte_cntr_data. + * @irq_ctrl_offset: offset to the BYTECNTVAL Register. + * @irq_name: IRQ name defined in DT. + */ +struct ctcu_byte_cntr { + bool enable; + bool reading; + bool reading_buf; + u32 thresh_val; + u64 total_size; + int irq_num; + atomic_t irq_cnt; + wait_queue_head_t wq; raw_spinlock_t spin_lock; - u32 atid_offset[ETR_MAX_NUM]; + u32 irq_ctrl_offset; + const char *irq_name; +}; + +struct ctcu_drvdata { + void __iomem *base; + struct clk *apb_clk; + struct device *dev; + struct coresight_device *csdev; + struct ctcu_byte_cntr byte_cntr_data[ETR_MAX_NUM]; + raw_spinlock_t spin_lock; + u32 atid_offset[ETR_MAX_NUM]; /* refcnt for each traceid of each sink */ - u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP]; + u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP]; + const struct tmc_read_ops *byte_cntr_read_ops; }; =20 +/* Byte-cntr functions */ +void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight= _path *path); +void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_= path *path); +void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata,= int port_num); + #endif diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtr= acing/coresight/coresight-tmc-etr.c index e8ecb3e087ab..c2a4ac3e37b3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1117,6 +1117,12 @@ static int __tmc_etr_enable_hw(struct tmc_drvdata *d= rvdata) return rc; } =20 +int tmc_etr_enable_hw_after_switching(struct tmc_drvdata *drvdata) +{ + return __tmc_etr_enable_hw(drvdata); +} +EXPORT_SYMBOL_GPL(tmc_etr_enable_hw_after_switching); + static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, struct etr_buf *etr_buf) { @@ -1163,6 +1169,10 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *= drvdata, ssize_t actual =3D len; struct etr_buf *etr_buf =3D drvdata->sysfs_buf; =20 + /* Reading the buffer from the buf_node if it exists*/ + if (drvdata->reading_node) + etr_buf =3D drvdata->reading_node->sysfs_buf; + if (pos + actual > etr_buf->len) actual =3D etr_buf->len - pos; if (actual <=3D 0) @@ -1226,6 +1236,12 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata = *drvdata) =20 } =20 +void tmc_etr_disable_hw_before_switching(struct tmc_drvdata *drvdata) +{ + __tmc_etr_disable_hw(drvdata); +} +EXPORT_SYMBOL_GPL(tmc_etr_disable_hw_before_switching); + void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { __tmc_etr_disable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracin= g/coresight/coresight-tmc.h index 2ad8e288c94b..6f42cd392e1b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -480,5 +480,7 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_dev= ice *csdev, extern const struct attribute_group coresight_etr_group; void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata); int tmc_create_etr_buf_node(struct tmc_drvdata *drvdata, struct etr_buf *a= lloc_buf); +int tmc_etr_enable_hw_after_switching(struct tmc_drvdata *drvdata); +void tmc_etr_disable_hw_before_switching(struct tmc_drvdata *drvdata); =20 #endif --=20 2.34.1