1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
4 */
5
6 #include <linux/amba/bus.h>
7 #include <linux/bitfield.h>
8 #include <linux/coresight.h>
9 #include <linux/device.h>
10 #include <linux/err.h>
11 #include <linux/fs.h>
12 #include <linux/io.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17
18 #include "coresight-priv.h"
19 #include "coresight-tpda.h"
20 #include "coresight-trace-id.h"
21 #include "coresight-tpdm.h"
22
23 DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
24
tpda_clear_element_size(struct coresight_device * csdev)25 static void tpda_clear_element_size(struct coresight_device *csdev)
26 {
27 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
28
29 drvdata->dsb_esize = 0;
30 drvdata->cmb_esize = 0;
31 }
32
tpda_set_element_size(struct tpda_drvdata * drvdata,u32 * val)33 static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val)
34 {
35 /* Clear all relevant fields */
36 *val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE);
37
38 if (drvdata->dsb_esize == 64)
39 *val |= TPDA_Pn_CR_DSBSIZE;
40 else if (drvdata->dsb_esize == 32)
41 *val &= ~TPDA_Pn_CR_DSBSIZE;
42
43 if (drvdata->cmb_esize == 64)
44 *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2);
45 else if (drvdata->cmb_esize == 32)
46 *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1);
47 else if (drvdata->cmb_esize == 8)
48 *val &= ~TPDA_Pn_CR_CMBSIZE;
49 }
50
51 /*
52 * Read the element size from the TPDM device. One TPDM must have at least one of the
53 * element size property.
54 * Returns
55 * 0 - The element size property is read
56 * Others - Cannot read the property of the element size
57 */
tpdm_read_element_size(struct tpda_drvdata * drvdata,struct coresight_device * csdev)58 static int tpdm_read_element_size(struct tpda_drvdata *drvdata,
59 struct coresight_device *csdev)
60 {
61 int rc = -EINVAL;
62 struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent);
63
64 if (tpdm_data->dsb) {
65 rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
66 "qcom,dsb-element-bits", &drvdata->dsb_esize);
67 if (rc)
68 goto out;
69 }
70
71 if (tpdm_data->cmb) {
72 rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
73 "qcom,cmb-element-bits", &drvdata->cmb_esize);
74 }
75
76 out:
77 if (rc)
78 dev_warn_once(&csdev->dev,
79 "Failed to read TPDM Element size: %d\n", rc);
80
81 return rc;
82 }
83
84 /*
85 * Search and read element data size from the TPDM node in
86 * the devicetree. Each input port of TPDA is connected to
87 * a TPDM. Different TPDM supports different types of dataset,
88 * and some may support more than one type of dataset.
89 * Parameter "inport" is used to pass in the input port number
90 * of TPDA, and it is set to -1 in the recursize call.
91 */
tpda_get_element_size(struct tpda_drvdata * drvdata,struct coresight_device * csdev,int inport)92 static int tpda_get_element_size(struct tpda_drvdata *drvdata,
93 struct coresight_device *csdev,
94 int inport)
95 {
96 int rc = 0;
97 int i;
98 struct coresight_device *in;
99
100 for (i = 0; i < csdev->pdata->nr_inconns; i++) {
101 in = csdev->pdata->in_conns[i]->src_dev;
102 if (!in)
103 continue;
104
105 /* Ignore the paths that do not match port */
106 if (inport >= 0 &&
107 csdev->pdata->in_conns[i]->dest_port != inport)
108 continue;
109
110 /*
111 * If this port has a hardcoded filter, use the source
112 * device directly.
113 */
114 if (csdev->pdata->in_conns[i]->filter_src_fwnode) {
115 in = csdev->pdata->in_conns[i]->filter_src_dev;
116 if (!in)
117 continue;
118 }
119
120 if (coresight_device_is_tpdm(in)) {
121 if (drvdata->dsb_esize || drvdata->cmb_esize)
122 return -EEXIST;
123 rc = tpdm_read_element_size(drvdata, in);
124 if (rc)
125 return rc;
126 } else {
127 /* Recurse down the path */
128 rc = tpda_get_element_size(drvdata, in, -1);
129 if (rc)
130 return rc;
131 }
132 }
133
134 return rc;
135 }
136
137 /* Settings pre enabling port control register */
tpda_enable_pre_port(struct tpda_drvdata * drvdata)138 static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
139 {
140 u32 val;
141
142 val = readl_relaxed(drvdata->base + TPDA_CR);
143 val &= ~TPDA_CR_ATID;
144 val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
145 writel_relaxed(val, drvdata->base + TPDA_CR);
146 }
147
tpda_enable_port(struct tpda_drvdata * drvdata,int port)148 static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
149 {
150 u32 val;
151 int rc;
152
153 val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
154 tpda_clear_element_size(drvdata->csdev);
155 rc = tpda_get_element_size(drvdata, drvdata->csdev, port);
156 if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) {
157 tpda_set_element_size(drvdata, &val);
158 /* Enable the port */
159 val |= TPDA_Pn_CR_ENA;
160 writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
161 } else if (rc == -EEXIST)
162 dev_warn_once(&drvdata->csdev->dev,
163 "Detected multiple TPDMs on port %d", port);
164 else
165 dev_warn_once(&drvdata->csdev->dev,
166 "Didn't find TPDM element size");
167
168 return rc;
169 }
170
__tpda_enable(struct tpda_drvdata * drvdata,int port)171 static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
172 {
173 int ret;
174
175 CS_UNLOCK(drvdata->base);
176
177 /*
178 * Only do pre-port enable for first port that calls enable when the
179 * device's main refcount is still 0
180 */
181 lockdep_assert_held(&drvdata->spinlock);
182 if (!drvdata->csdev->refcnt)
183 tpda_enable_pre_port(drvdata);
184
185 ret = tpda_enable_port(drvdata, port);
186 CS_LOCK(drvdata->base);
187
188 return ret;
189 }
190
tpda_enable(struct coresight_device * csdev,struct coresight_connection * in,struct coresight_connection * out)191 static int tpda_enable(struct coresight_device *csdev,
192 struct coresight_connection *in,
193 struct coresight_connection *out)
194 {
195 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
196 int ret = 0;
197
198 spin_lock(&drvdata->spinlock);
199 if (in->dest_refcnt == 0) {
200 ret = __tpda_enable(drvdata, in->dest_port);
201 if (!ret) {
202 in->dest_refcnt++;
203 csdev->refcnt++;
204 dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
205 }
206 }
207
208 spin_unlock(&drvdata->spinlock);
209 return ret;
210 }
211
__tpda_disable(struct tpda_drvdata * drvdata,int port)212 static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
213 {
214 u32 val;
215
216 CS_UNLOCK(drvdata->base);
217
218 val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
219 val &= ~TPDA_Pn_CR_ENA;
220 writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
221
222 CS_LOCK(drvdata->base);
223 }
224
tpda_disable(struct coresight_device * csdev,struct coresight_connection * in,struct coresight_connection * out)225 static void tpda_disable(struct coresight_device *csdev,
226 struct coresight_connection *in,
227 struct coresight_connection *out)
228 {
229 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
230
231 spin_lock(&drvdata->spinlock);
232 if (--in->dest_refcnt == 0) {
233 __tpda_disable(drvdata, in->dest_port);
234 csdev->refcnt--;
235 }
236 spin_unlock(&drvdata->spinlock);
237
238 dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
239 }
240
tpda_trace_id(struct coresight_device * csdev,__maybe_unused enum cs_mode mode,__maybe_unused struct coresight_device * sink)241 static int tpda_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
242 __maybe_unused struct coresight_device *sink)
243 {
244 struct tpda_drvdata *drvdata;
245
246 drvdata = dev_get_drvdata(csdev->dev.parent);
247
248 return drvdata->atid;
249 }
250
251 static const struct coresight_ops_link tpda_link_ops = {
252 .enable = tpda_enable,
253 .disable = tpda_disable,
254 };
255
256 static const struct coresight_ops tpda_cs_ops = {
257 .trace_id = tpda_trace_id,
258 .link_ops = &tpda_link_ops,
259 };
260
tpda_init_default_data(struct tpda_drvdata * drvdata)261 static int tpda_init_default_data(struct tpda_drvdata *drvdata)
262 {
263 int atid;
264 /*
265 * TPDA must has a unique atid. This atid can uniquely
266 * identify the TPDM trace source connected to the TPDA.
267 * The TPDMs which are connected to same TPDA share the
268 * same trace-id. When TPDA does packetization, different
269 * port will have unique channel number for decoding.
270 */
271 atid = coresight_trace_id_get_system_id();
272 if (atid < 0)
273 return atid;
274
275 drvdata->atid = atid;
276 return 0;
277 }
278
tpda_probe(struct amba_device * adev,const struct amba_id * id)279 static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
280 {
281 int ret;
282 struct device *dev = &adev->dev;
283 struct coresight_platform_data *pdata;
284 struct tpda_drvdata *drvdata;
285 struct coresight_desc desc = { 0 };
286 void __iomem *base;
287
288 pdata = coresight_get_platform_data(dev);
289 if (IS_ERR(pdata))
290 return PTR_ERR(pdata);
291 adev->dev.platform_data = pdata;
292
293 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
294 if (!drvdata)
295 return -ENOMEM;
296
297 drvdata->dev = &adev->dev;
298 dev_set_drvdata(dev, drvdata);
299
300 base = devm_ioremap_resource(dev, &adev->res);
301 if (IS_ERR(base))
302 return PTR_ERR(base);
303 drvdata->base = base;
304
305 spin_lock_init(&drvdata->spinlock);
306
307 ret = tpda_init_default_data(drvdata);
308 if (ret)
309 return ret;
310
311 desc.name = coresight_alloc_device_name(&tpda_devs, dev);
312 if (!desc.name)
313 return -ENOMEM;
314 desc.type = CORESIGHT_DEV_TYPE_LINK;
315 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
316 desc.ops = &tpda_cs_ops;
317 desc.pdata = adev->dev.platform_data;
318 desc.dev = &adev->dev;
319 desc.access = CSDEV_ACCESS_IOMEM(base);
320 drvdata->csdev = coresight_register(&desc);
321 if (IS_ERR(drvdata->csdev))
322 return PTR_ERR(drvdata->csdev);
323
324 pm_runtime_put(&adev->dev);
325
326 dev_dbg(drvdata->dev, "TPDA initialized\n");
327 return 0;
328 }
329
tpda_remove(struct amba_device * adev)330 static void tpda_remove(struct amba_device *adev)
331 {
332 struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev);
333
334 coresight_trace_id_put_system_id(drvdata->atid);
335 coresight_unregister(drvdata->csdev);
336 }
337
338 /*
339 * Different TPDA has different periph id.
340 * The difference is 0-7 bits' value. So ignore 0-7 bits.
341 */
342 static const struct amba_id tpda_ids[] = {
343 {
344 .id = 0x000f0f00,
345 .mask = 0x000fff00,
346 },
347 { 0, 0, NULL },
348 };
349
350 static struct amba_driver tpda_driver = {
351 .drv = {
352 .name = "coresight-tpda",
353 .suppress_bind_attrs = true,
354 },
355 .probe = tpda_probe,
356 .remove = tpda_remove,
357 .id_table = tpda_ids,
358 };
359
360 module_amba_driver(tpda_driver);
361
362 MODULE_LICENSE("GPL");
363 MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");
364