File Index
Files and directories
Sign in
FILE

coresight-ctcu-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwtracing/coresight/coresight-ctcu-core.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  4 */
  5
  6#include <linux/clk.h>
  7#include <linux/coresight.h>
  8#include <linux/device.h>
  9#include <linux/err.h>
 10#include <linux/kernel.h>
 11#include <linux/init.h>
 12#include <linux/io.h>
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/platform_device.h>
 16#include <linux/pm_runtime.h>
 17#include <linux/slab.h>
 18
 19#include "coresight-ctcu.h"
 20#include "coresight-priv.h"
 21
 22DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu");
 23
 24#define ctcu_writel(drvdata, val, offset)	__raw_writel((val), drvdata->base + offset)
 25#define ctcu_readl(drvdata, offset)		__raw_readl(drvdata->base + offset)
 26
 27/*
 28 * The TMC Coresight Control Unit utilizes four ATID registers to control the data
 29 * filter function based on the trace ID for each TMC ETR sink. The length of each
 30 * ATID register is 32 bits. Therefore, an ETR device has a 128-bit long field
 31 * in CTCU. Each trace ID is represented by one bit in that filed.
 32 * e.g. ETR0ATID0 layout, set bit 5 for traceid 5
 33 *                                           bit5
 34 * ------------------------------------------------------
 35 * |   |28|   |24|   |20|   |16|   |12|   |8|  1|4|   |0|
 36 * ------------------------------------------------------
 37 *
 38 * e.g. ETR0:
 39 * 127                     0 from ATID_offset for ETR0ATID0
 40 * -------------------------
 41 * |ATID3|ATID2|ATID1|ATID0|
 42 */
 43#define CTCU_ATID_REG_OFFSET(traceid, atid_offset) \
 44		((traceid / 32) * 4 + atid_offset)
 45
 46#define CTCU_ATID_REG_BIT(traceid)	(traceid % 32)
 47#define CTCU_ATID_REG_SIZE		0x10
 48#define CTCU_ETR0_ATID0			0xf8
 49#define CTCU_ETR1_ATID0			0x108
 50
 51static const struct ctcu_etr_config sa8775p_etr_cfgs[] = {
 52	{
 53		.atid_offset	= CTCU_ETR0_ATID0,
 54		.port_num	= 0,
 55	},
 56	{
 57		.atid_offset	= CTCU_ETR1_ATID0,
 58		.port_num	= 1,
 59	},
 60};
 61
 62static const struct ctcu_config sa8775p_cfgs = {
 63	.etr_cfgs	= sa8775p_etr_cfgs,
 64	.num_etr_config	= ARRAY_SIZE(sa8775p_etr_cfgs),
 65};
 66
 67static void ctcu_program_atid_register(struct ctcu_drvdata *drvdata, u32 reg_offset,
 68				       u8 bit, bool enable)
 69{
 70	u32 val;
 71
 72	CS_UNLOCK(drvdata->base);
 73	val = ctcu_readl(drvdata, reg_offset);
 74	if (enable)
 75		val |= BIT(bit);
 76	else
 77		val &= ~BIT(bit);
 78
 79	ctcu_writel(drvdata, val, reg_offset);
 80	CS_LOCK(drvdata->base);
 81}
 82
 83/*
 84 * __ctcu_set_etr_traceid: Set bit in the ATID register based on trace ID when enable is true.
 85 * Reset the bit of the ATID register based on trace ID when enable is false.
 86 *
 87 * @csdev:	coresight_device of CTCU.
 88 * @traceid:	trace ID of the source tracer.
 89 * @port_num:	port number connected to TMC ETR sink.
 90 * @enable:	True for set bit and false for reset bit.
 91 *
 92 * Returns 0 indicates success. Non-zero result means failure.
 93 */
 94static int __ctcu_set_etr_traceid(struct coresight_device *csdev, u8 traceid, int port_num,
 95				  bool enable)
 96{
 97	struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 98	u32 atid_offset, reg_offset;
 99	u8 refcnt, bit;
100
101	atid_offset = drvdata->atid_offset[port_num];
102	if (atid_offset == 0)
103		return -EINVAL;
104
105	bit = CTCU_ATID_REG_BIT(traceid);
106	reg_offset = CTCU_ATID_REG_OFFSET(traceid, atid_offset);
107	if (reg_offset - atid_offset > CTCU_ATID_REG_SIZE)
108		return -EINVAL;
109
110	guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
111	refcnt = drvdata->traceid_refcnt[port_num][traceid];
112	/* Only program the atid register when the refcnt value is 1 or 0 */
113	if ((enable && !refcnt++) || (!enable && !--refcnt))
114		ctcu_program_atid_register(drvdata, reg_offset, bit, enable);
115
116	drvdata->traceid_refcnt[port_num][traceid] = refcnt;
117
118	return 0;
119}
120
121/*
122 * Searching the sink device from helper's view in case there are multiple helper devices
123 * connected to the sink device.
124 */
125static int ctcu_get_active_port(struct coresight_device *sink, struct coresight_device *helper)
126{
127	struct coresight_platform_data *pdata = helper->pdata;
128	int i;
129
130	for (i = 0; i < pdata->nr_inconns; ++i) {
131		if (pdata->in_conns[i]->src_dev == sink)
132			return pdata->in_conns[i]->dest_port;
133	}
134
135	return -EINVAL;
136}
137
138static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight_path *path,
139				bool enable)
140{
141	struct coresight_device *sink = coresight_get_sink(path);
142	u8 traceid = path->trace_id;
143	int port_num;
144
145	if ((sink == NULL) || !IS_VALID_CS_TRACE_ID(traceid)) {
146		dev_err(&csdev->dev, "Invalid sink device or trace ID\n");
147		return -EINVAL;
148	}
149
150	port_num = ctcu_get_active_port(sink, csdev);
151	if (port_num < 0)
152		return -EINVAL;
153
154	dev_dbg(&csdev->dev, "traceid is %d\n", traceid);
155
156	return __ctcu_set_etr_traceid(csdev, traceid, port_num, enable);
157}
158
159static int ctcu_enable(struct coresight_device *csdev, enum cs_mode mode,
160		       struct coresight_path *path)
161{
162	return ctcu_set_etr_traceid(csdev, path, true);
163}
164
165static int ctcu_disable(struct coresight_device *csdev, struct coresight_path *path)
166{
167	return ctcu_set_etr_traceid(csdev, path, false);
168}
169
170static const struct coresight_ops_helper ctcu_helper_ops = {
171	.enable = ctcu_enable,
172	.disable = ctcu_disable,
173};
174
175static const struct coresight_ops ctcu_ops = {
176	.helper_ops = &ctcu_helper_ops,
177};
178
179static int ctcu_probe(struct platform_device *pdev)
180{
181	const struct ctcu_etr_config *etr_cfg;
182	struct coresight_platform_data *pdata;
183	struct coresight_desc desc = { 0 };
184	struct device *dev = &pdev->dev;
185	const struct ctcu_config *cfgs;
186	struct ctcu_drvdata *drvdata;
187	void __iomem *base;
188	int i, ret;
189
190	desc.name = coresight_alloc_device_name(&ctcu_devs, dev);
191	if (!desc.name)
192		return -ENOMEM;
193
194	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
195	if (!drvdata)
196		return -ENOMEM;
197
198	pdata = coresight_get_platform_data(dev);
199	if (IS_ERR(pdata))
200		return PTR_ERR(pdata);
201	dev->platform_data = pdata;
202
203	base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
204	if (IS_ERR(base))
205		return PTR_ERR(base);
206
207	ret = coresight_get_enable_clocks(dev, &drvdata->apb_clk, NULL);
208	if (ret)
209		return ret;
210
211	cfgs = of_device_get_match_data(dev);
212	if (cfgs) {
213		if (cfgs->num_etr_config <= ETR_MAX_NUM) {
214			for (i = 0; i < cfgs->num_etr_config; i++) {
215				etr_cfg = &cfgs->etr_cfgs[i];
216				drvdata->atid_offset[i] = etr_cfg->atid_offset;
217			}
218		}
219	}
220
221	drvdata->base = base;
222	drvdata->dev = dev;
223	platform_set_drvdata(pdev, drvdata);
224
225	desc.type = CORESIGHT_DEV_TYPE_HELPER;
226	desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CTCU;
227	desc.pdata = pdata;
228	desc.dev = dev;
229	desc.ops = &ctcu_ops;
230	desc.access = CSDEV_ACCESS_IOMEM(base);
231
232	drvdata->csdev = coresight_register(&desc);
233	if (IS_ERR(drvdata->csdev))
234		return PTR_ERR(drvdata->csdev);
235
236	return 0;
237}
238
239static void ctcu_remove(struct platform_device *pdev)
240{
241	struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
242
243	coresight_unregister(drvdata->csdev);
244}
245
246static int ctcu_platform_probe(struct platform_device *pdev)
247{
248	int ret;
249
250	pm_runtime_get_noresume(&pdev->dev);
251	pm_runtime_set_active(&pdev->dev);
252	pm_runtime_enable(&pdev->dev);
253
254	ret = ctcu_probe(pdev);
255	pm_runtime_put(&pdev->dev);
256	if (ret)
257		pm_runtime_disable(&pdev->dev);
258
259	return ret;
260}
261
262static void ctcu_platform_remove(struct platform_device *pdev)
263{
264	struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
265
266	if (WARN_ON(!drvdata))
267		return;
268
269	ctcu_remove(pdev);
270	pm_runtime_disable(&pdev->dev);
271}
272
273#ifdef CONFIG_PM
274static int ctcu_runtime_suspend(struct device *dev)
275{
276	struct ctcu_drvdata *drvdata = dev_get_drvdata(dev);
277
278	clk_disable_unprepare(drvdata->apb_clk);
279
280	return 0;
281}
282
283static int ctcu_runtime_resume(struct device *dev)
284{
285	struct ctcu_drvdata *drvdata = dev_get_drvdata(dev);
286
287	return clk_prepare_enable(drvdata->apb_clk);
288}
289#endif
290
291static const struct dev_pm_ops ctcu_dev_pm_ops = {
292	SET_RUNTIME_PM_OPS(ctcu_runtime_suspend, ctcu_runtime_resume, NULL)
293};
294
295static const struct of_device_id ctcu_match[] = {
296	{.compatible = "qcom,sa8775p-ctcu", .data = &sa8775p_cfgs},
297	{}
298};
299
300static struct platform_driver ctcu_driver = {
301	.probe          = ctcu_platform_probe,
302	.remove         = ctcu_platform_remove,
303	.driver         = {
304		.name   = "coresight-ctcu",
305		.of_match_table = ctcu_match,
306		.pm	= &ctcu_dev_pm_ops,
307		.suppress_bind_attrs = true,
308	},
309};
310module_platform_driver(ctcu_driver);
311
312MODULE_LICENSE("GPL");
313MODULE_DESCRIPTION("CoreSight TMC Control Unit driver");
~/ Read-only file preview.