Merge tag 'drm-intel-gt-next-2024-02-15' of git://anongit.freedesktop.org/drm/drm...
[linux-2.6-microblaze.git] / drivers / hwtracing / coresight / coresight-tpda.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2023 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
22 DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
23
24 static bool coresight_device_is_tpdm(struct coresight_device *csdev)
25 {
26         return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) &&
27                (csdev->subtype.source_subtype ==
28                         CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
29 }
30
31 /*
32  * Read the DSB element size from the TPDM device
33  * Returns
34  *    The dsb element size read from the devicetree if available.
35  *    0 - Otherwise, with a warning once.
36  */
37 static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
38 {
39         int rc = 0;
40         u8 size = 0;
41
42         rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent),
43                         "qcom,dsb-element-size", &size);
44         if (rc)
45                 dev_warn_once(&csdev->dev,
46                         "Failed to read TPDM DSB Element size: %d\n", rc);
47
48         return size;
49 }
50
51 /*
52  * Search and read element data size from the TPDM node in
53  * the devicetree. Each input port of TPDA is connected to
54  * a TPDM. Different TPDM supports different types of dataset,
55  * and some may support more than one type of dataset.
56  * Parameter "inport" is used to pass in the input port number
57  * of TPDA, and it is set to -1 in the recursize call.
58  */
59 static int tpda_get_element_size(struct coresight_device *csdev,
60                                  int inport)
61 {
62         int dsb_size = -ENOENT;
63         int i, size;
64         struct coresight_device *in;
65
66         for (i = 0; i < csdev->pdata->nr_inconns; i++) {
67                 in = csdev->pdata->in_conns[i]->src_dev;
68                 if (!in)
69                         continue;
70
71                 /* Ignore the paths that do not match port */
72                 if (inport > 0 &&
73                     csdev->pdata->in_conns[i]->dest_port != inport)
74                         continue;
75
76                 if (coresight_device_is_tpdm(in)) {
77                         size = tpdm_read_dsb_element_size(in);
78                 } else {
79                         /* Recurse down the path */
80                         size = tpda_get_element_size(in, -1);
81                 }
82
83                 if (size < 0)
84                         return size;
85
86                 if (dsb_size < 0) {
87                         /* Found a size, save it. */
88                         dsb_size = size;
89                 } else {
90                         /* Found duplicate TPDMs */
91                         return -EEXIST;
92                 }
93         }
94
95         return dsb_size;
96 }
97
98 /* Settings pre enabling port control register */
99 static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
100 {
101         u32 val;
102
103         val = readl_relaxed(drvdata->base + TPDA_CR);
104         val &= ~TPDA_CR_ATID;
105         val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
106         writel_relaxed(val, drvdata->base + TPDA_CR);
107 }
108
109 static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
110 {
111         u32 val;
112         int size;
113
114         val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
115         /*
116          * Configure aggregator port n DSB data set element size
117          * Set the bit to 0 if the size is 32
118          * Set the bit to 1 if the size is 64
119          */
120         size = tpda_get_element_size(drvdata->csdev, port);
121         switch (size) {
122         case 32:
123                 val &= ~TPDA_Pn_CR_DSBSIZE;
124                 break;
125         case 64:
126                 val |= TPDA_Pn_CR_DSBSIZE;
127                 break;
128         case 0:
129                 return -EEXIST;
130         case -EEXIST:
131                 dev_warn_once(&drvdata->csdev->dev,
132                         "Detected multiple TPDMs on port %d", -EEXIST);
133                 return -EEXIST;
134         default:
135                 return -EINVAL;
136         }
137
138         /* Enable the port */
139         val |= TPDA_Pn_CR_ENA;
140         writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
141
142         return 0;
143 }
144
145 static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
146 {
147         int ret;
148
149         CS_UNLOCK(drvdata->base);
150
151         if (!drvdata->csdev->enable)
152                 tpda_enable_pre_port(drvdata);
153
154         ret = tpda_enable_port(drvdata, port);
155         CS_LOCK(drvdata->base);
156
157         return ret;
158 }
159
160 static int tpda_enable(struct coresight_device *csdev,
161                        struct coresight_connection *in,
162                        struct coresight_connection *out)
163 {
164         struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
165         int ret = 0;
166
167         spin_lock(&drvdata->spinlock);
168         if (atomic_read(&in->dest_refcnt) == 0) {
169                 ret = __tpda_enable(drvdata, in->dest_port);
170                 if (!ret) {
171                         atomic_inc(&in->dest_refcnt);
172                         dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
173                 }
174         }
175
176         spin_unlock(&drvdata->spinlock);
177         return ret;
178 }
179
180 static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
181 {
182         u32 val;
183
184         CS_UNLOCK(drvdata->base);
185
186         val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
187         val &= ~TPDA_Pn_CR_ENA;
188         writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
189
190         CS_LOCK(drvdata->base);
191 }
192
193 static void tpda_disable(struct coresight_device *csdev,
194                          struct coresight_connection *in,
195                          struct coresight_connection *out)
196 {
197         struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
198
199         spin_lock(&drvdata->spinlock);
200         if (atomic_dec_return(&in->dest_refcnt) == 0)
201                 __tpda_disable(drvdata, in->dest_port);
202
203         spin_unlock(&drvdata->spinlock);
204
205         dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
206 }
207
208 static const struct coresight_ops_link tpda_link_ops = {
209         .enable         = tpda_enable,
210         .disable        = tpda_disable,
211 };
212
213 static const struct coresight_ops tpda_cs_ops = {
214         .link_ops       = &tpda_link_ops,
215 };
216
217 static int tpda_init_default_data(struct tpda_drvdata *drvdata)
218 {
219         int atid;
220         /*
221          * TPDA must has a unique atid. This atid can uniquely
222          * identify the TPDM trace source connected to the TPDA.
223          * The TPDMs which are connected to same TPDA share the
224          * same trace-id. When TPDA does packetization, different
225          * port will have unique channel number for decoding.
226          */
227         atid = coresight_trace_id_get_system_id();
228         if (atid < 0)
229                 return atid;
230
231         drvdata->atid = atid;
232         return 0;
233 }
234
235 static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
236 {
237         int ret;
238         struct device *dev = &adev->dev;
239         struct coresight_platform_data *pdata;
240         struct tpda_drvdata *drvdata;
241         struct coresight_desc desc = { 0 };
242         void __iomem *base;
243
244         pdata = coresight_get_platform_data(dev);
245         if (IS_ERR(pdata))
246                 return PTR_ERR(pdata);
247         adev->dev.platform_data = pdata;
248
249         drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
250         if (!drvdata)
251                 return -ENOMEM;
252
253         drvdata->dev = &adev->dev;
254         dev_set_drvdata(dev, drvdata);
255
256         base = devm_ioremap_resource(dev, &adev->res);
257         if (IS_ERR(base))
258                 return PTR_ERR(base);
259         drvdata->base = base;
260
261         spin_lock_init(&drvdata->spinlock);
262
263         ret = tpda_init_default_data(drvdata);
264         if (ret)
265                 return ret;
266
267         desc.name = coresight_alloc_device_name(&tpda_devs, dev);
268         if (!desc.name)
269                 return -ENOMEM;
270         desc.type = CORESIGHT_DEV_TYPE_LINK;
271         desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
272         desc.ops = &tpda_cs_ops;
273         desc.pdata = adev->dev.platform_data;
274         desc.dev = &adev->dev;
275         desc.access = CSDEV_ACCESS_IOMEM(base);
276         drvdata->csdev = coresight_register(&desc);
277         if (IS_ERR(drvdata->csdev))
278                 return PTR_ERR(drvdata->csdev);
279
280         pm_runtime_put(&adev->dev);
281
282         dev_dbg(drvdata->dev, "TPDA initialized\n");
283         return 0;
284 }
285
286 static void tpda_remove(struct amba_device *adev)
287 {
288         struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev);
289
290         coresight_trace_id_put_system_id(drvdata->atid);
291         coresight_unregister(drvdata->csdev);
292 }
293
294 /*
295  * Different TPDA has different periph id.
296  * The difference is 0-7 bits' value. So ignore 0-7 bits.
297  */
298 static struct amba_id tpda_ids[] = {
299         {
300                 .id     = 0x000f0f00,
301                 .mask   = 0x000fff00,
302         },
303         { 0, 0},
304 };
305
306 static struct amba_driver tpda_driver = {
307         .drv = {
308                 .name   = "coresight-tpda",
309                 .owner  = THIS_MODULE,
310                 .suppress_bind_attrs = true,
311         },
312         .probe          = tpda_probe,
313         .remove         = tpda_remove,
314         .id_table       = tpda_ids,
315 };
316
317 module_amba_driver(tpda_driver);
318
319 MODULE_LICENSE("GPL");
320 MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");