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