1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
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>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
16 #include <linux/platform_device.h>
18 #include "coresight-priv.h"
19 #include "coresight-tpda.h"
20 #include "coresight-trace-id.h"
21 #include "coresight-tpdm.h"
23 DEFINE_CORESIGHT_DEVLIST(tpda_devs
, "tpda");
25 static bool coresight_device_is_tpdm(struct coresight_device
*csdev
)
27 return (csdev
->type
== CORESIGHT_DEV_TYPE_SOURCE
) &&
28 (csdev
->subtype
.source_subtype
==
29 CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM
);
32 static void tpda_clear_element_size(struct coresight_device
*csdev
)
34 struct tpda_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
36 drvdata
->dsb_esize
= 0;
37 drvdata
->cmb_esize
= 0;
40 static void tpda_set_element_size(struct tpda_drvdata
*drvdata
, u32
*val
)
42 /* Clear all relevant fields */
43 *val
&= ~(TPDA_Pn_CR_DSBSIZE
| TPDA_Pn_CR_CMBSIZE
);
45 if (drvdata
->dsb_esize
== 64)
46 *val
|= TPDA_Pn_CR_DSBSIZE
;
47 else if (drvdata
->dsb_esize
== 32)
48 *val
&= ~TPDA_Pn_CR_DSBSIZE
;
50 if (drvdata
->cmb_esize
== 64)
51 *val
|= FIELD_PREP(TPDA_Pn_CR_CMBSIZE
, 0x2);
52 else if (drvdata
->cmb_esize
== 32)
53 *val
|= FIELD_PREP(TPDA_Pn_CR_CMBSIZE
, 0x1);
54 else if (drvdata
->cmb_esize
== 8)
55 *val
&= ~TPDA_Pn_CR_CMBSIZE
;
59 * Read the element size from the TPDM device. One TPDM must have at least one of the
60 * element size property.
62 * 0 - The element size property is read
63 * Others - Cannot read the property of the element size
65 static int tpdm_read_element_size(struct tpda_drvdata
*drvdata
,
66 struct coresight_device
*csdev
)
69 struct tpdm_drvdata
*tpdm_data
= dev_get_drvdata(csdev
->dev
.parent
);
71 if (tpdm_has_dsb_dataset(tpdm_data
)) {
72 rc
= fwnode_property_read_u32(dev_fwnode(csdev
->dev
.parent
),
73 "qcom,dsb-element-bits", &drvdata
->dsb_esize
);
75 if (tpdm_has_cmb_dataset(tpdm_data
)) {
76 rc
= fwnode_property_read_u32(dev_fwnode(csdev
->dev
.parent
),
77 "qcom,cmb-element-bits", &drvdata
->cmb_esize
);
81 dev_warn_once(&csdev
->dev
,
82 "Failed to read TPDM Element size: %d\n", rc
);
88 * Search and read element data size from the TPDM node in
89 * the devicetree. Each input port of TPDA is connected to
90 * a TPDM. Different TPDM supports different types of dataset,
91 * and some may support more than one type of dataset.
92 * Parameter "inport" is used to pass in the input port number
93 * of TPDA, and it is set to -1 in the recursize call.
95 static int tpda_get_element_size(struct tpda_drvdata
*drvdata
,
96 struct coresight_device
*csdev
,
101 struct coresight_device
*in
;
103 for (i
= 0; i
< csdev
->pdata
->nr_inconns
; i
++) {
104 in
= csdev
->pdata
->in_conns
[i
]->src_dev
;
108 /* Ignore the paths that do not match port */
110 csdev
->pdata
->in_conns
[i
]->dest_port
!= inport
)
113 if (coresight_device_is_tpdm(in
)) {
114 if (drvdata
->dsb_esize
|| drvdata
->cmb_esize
)
116 rc
= tpdm_read_element_size(drvdata
, in
);
120 /* Recurse down the path */
121 rc
= tpda_get_element_size(drvdata
, in
, -1);
131 /* Settings pre enabling port control register */
132 static void tpda_enable_pre_port(struct tpda_drvdata
*drvdata
)
136 val
= readl_relaxed(drvdata
->base
+ TPDA_CR
);
137 val
&= ~TPDA_CR_ATID
;
138 val
|= FIELD_PREP(TPDA_CR_ATID
, drvdata
->atid
);
139 writel_relaxed(val
, drvdata
->base
+ TPDA_CR
);
142 static int tpda_enable_port(struct tpda_drvdata
*drvdata
, int port
)
147 val
= readl_relaxed(drvdata
->base
+ TPDA_Pn_CR(port
));
148 tpda_clear_element_size(drvdata
->csdev
);
149 rc
= tpda_get_element_size(drvdata
, drvdata
->csdev
, port
);
150 if (!rc
&& (drvdata
->dsb_esize
|| drvdata
->cmb_esize
)) {
151 tpda_set_element_size(drvdata
, &val
);
152 /* Enable the port */
153 val
|= TPDA_Pn_CR_ENA
;
154 writel_relaxed(val
, drvdata
->base
+ TPDA_Pn_CR(port
));
155 } else if (rc
== -EEXIST
)
156 dev_warn_once(&drvdata
->csdev
->dev
,
157 "Detected multiple TPDMs on port %d", port
);
159 dev_warn_once(&drvdata
->csdev
->dev
,
160 "Didn't find TPDM element size");
165 static int __tpda_enable(struct tpda_drvdata
*drvdata
, int port
)
169 CS_UNLOCK(drvdata
->base
);
172 * Only do pre-port enable for first port that calls enable when the
173 * device's main refcount is still 0
175 lockdep_assert_held(&drvdata
->spinlock
);
176 if (!drvdata
->csdev
->refcnt
)
177 tpda_enable_pre_port(drvdata
);
179 ret
= tpda_enable_port(drvdata
, port
);
180 CS_LOCK(drvdata
->base
);
185 static int tpda_enable(struct coresight_device
*csdev
,
186 struct coresight_connection
*in
,
187 struct coresight_connection
*out
)
189 struct tpda_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
192 spin_lock(&drvdata
->spinlock
);
193 if (atomic_read(&in
->dest_refcnt
) == 0) {
194 ret
= __tpda_enable(drvdata
, in
->dest_port
);
196 atomic_inc(&in
->dest_refcnt
);
198 dev_dbg(drvdata
->dev
, "TPDA inport %d enabled.\n", in
->dest_port
);
202 spin_unlock(&drvdata
->spinlock
);
206 static void __tpda_disable(struct tpda_drvdata
*drvdata
, int port
)
210 CS_UNLOCK(drvdata
->base
);
212 val
= readl_relaxed(drvdata
->base
+ TPDA_Pn_CR(port
));
213 val
&= ~TPDA_Pn_CR_ENA
;
214 writel_relaxed(val
, drvdata
->base
+ TPDA_Pn_CR(port
));
216 CS_LOCK(drvdata
->base
);
219 static void tpda_disable(struct coresight_device
*csdev
,
220 struct coresight_connection
*in
,
221 struct coresight_connection
*out
)
223 struct tpda_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
225 spin_lock(&drvdata
->spinlock
);
226 if (atomic_dec_return(&in
->dest_refcnt
) == 0) {
227 __tpda_disable(drvdata
, in
->dest_port
);
230 spin_unlock(&drvdata
->spinlock
);
232 dev_dbg(drvdata
->dev
, "TPDA inport %d disabled\n", in
->dest_port
);
235 static const struct coresight_ops_link tpda_link_ops
= {
236 .enable
= tpda_enable
,
237 .disable
= tpda_disable
,
240 static const struct coresight_ops tpda_cs_ops
= {
241 .link_ops
= &tpda_link_ops
,
244 static int tpda_init_default_data(struct tpda_drvdata
*drvdata
)
248 * TPDA must has a unique atid. This atid can uniquely
249 * identify the TPDM trace source connected to the TPDA.
250 * The TPDMs which are connected to same TPDA share the
251 * same trace-id. When TPDA does packetization, different
252 * port will have unique channel number for decoding.
254 atid
= coresight_trace_id_get_system_id();
258 drvdata
->atid
= atid
;
262 static int tpda_probe(struct amba_device
*adev
, const struct amba_id
*id
)
265 struct device
*dev
= &adev
->dev
;
266 struct coresight_platform_data
*pdata
;
267 struct tpda_drvdata
*drvdata
;
268 struct coresight_desc desc
= { 0 };
271 pdata
= coresight_get_platform_data(dev
);
273 return PTR_ERR(pdata
);
274 adev
->dev
.platform_data
= pdata
;
276 drvdata
= devm_kzalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
280 drvdata
->dev
= &adev
->dev
;
281 dev_set_drvdata(dev
, drvdata
);
283 base
= devm_ioremap_resource(dev
, &adev
->res
);
285 return PTR_ERR(base
);
286 drvdata
->base
= base
;
288 spin_lock_init(&drvdata
->spinlock
);
290 ret
= tpda_init_default_data(drvdata
);
294 desc
.name
= coresight_alloc_device_name(&tpda_devs
, dev
);
297 desc
.type
= CORESIGHT_DEV_TYPE_LINK
;
298 desc
.subtype
.link_subtype
= CORESIGHT_DEV_SUBTYPE_LINK_MERG
;
299 desc
.ops
= &tpda_cs_ops
;
300 desc
.pdata
= adev
->dev
.platform_data
;
301 desc
.dev
= &adev
->dev
;
302 desc
.access
= CSDEV_ACCESS_IOMEM(base
);
303 drvdata
->csdev
= coresight_register(&desc
);
304 if (IS_ERR(drvdata
->csdev
))
305 return PTR_ERR(drvdata
->csdev
);
307 pm_runtime_put(&adev
->dev
);
309 dev_dbg(drvdata
->dev
, "TPDA initialized\n");
313 static void tpda_remove(struct amba_device
*adev
)
315 struct tpda_drvdata
*drvdata
= dev_get_drvdata(&adev
->dev
);
317 coresight_trace_id_put_system_id(drvdata
->atid
);
318 coresight_unregister(drvdata
->csdev
);
322 * Different TPDA has different periph id.
323 * The difference is 0-7 bits' value. So ignore 0-7 bits.
325 static struct amba_id tpda_ids
[] = {
333 static struct amba_driver tpda_driver
= {
335 .name
= "coresight-tpda",
336 .suppress_bind_attrs
= true,
339 .remove
= tpda_remove
,
340 .id_table
= tpda_ids
,
343 module_amba_driver(tpda_driver
);
345 MODULE_LICENSE("GPL");
346 MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");