xref: /linux/drivers/hwtracing/coresight/coresight-tnoc.c (revision e3966940559d52aa1800a008dcfeec218dd31f88)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6  #include <linux/amba/bus.h>
7  #include <linux/coresight.h>
8  #include <linux/device.h>
9  #include <linux/io.h>
10  #include <linux/kernel.h>
11  #include <linux/module.h>
12  #include <linux/of.h>
13  #include <linux/platform_device.h>
14 
15 #include "coresight-priv.h"
16 #include "coresight-trace-id.h"
17 
18 #define TRACE_NOC_CTRL      0x008
19 #define TRACE_NOC_XLD       0x010
20 #define TRACE_NOC_FREQVAL   0x018
21 #define TRACE_NOC_SYNCR     0x020
22 
23 /* Enable generation of output ATB traffic.*/
24 #define TRACE_NOC_CTRL_PORTEN   BIT(0)
25 /* Sets the type of issued ATB FLAG packets.*/
26 #define TRACE_NOC_CTRL_FLAGTYPE BIT(7)
27 /* Sets the type of issued ATB FREQ packet*/
28 #define TRACE_NOC_CTRL_FREQTYPE BIT(8)
29 
30 #define TRACE_NOC_SYNC_INTERVAL	0xFFFF
31 
32 /*
33  * struct trace_noc_drvdata - specifics associated to a trace noc component
34  * @base:      memory mapped base address for this component.
35  * @dev:       device node for trace_noc_drvdata.
36  * @csdev:     component vitals needed by the framework.
37  * @spinlock:  serialize enable/disable operation.
38  * @atid:      id for the trace packet.
39  */
40 struct trace_noc_drvdata {
41 	void __iomem		*base;
42 	struct device		*dev;
43 	struct coresight_device	*csdev;
44 	spinlock_t		spinlock;
45 	u32			atid;
46 };
47 
48 DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc");
49 
50 static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata)
51 {
52 	u32 val;
53 
54 	/* Set ATID */
55 	writel_relaxed(drvdata->atid, drvdata->base + TRACE_NOC_XLD);
56 
57 	/* Set the data word count between 'SYNC' packets */
58 	writel_relaxed(TRACE_NOC_SYNC_INTERVAL, drvdata->base + TRACE_NOC_SYNCR);
59 
60 	/* Set the Control register:
61 	 * - Set the FLAG packets to 'FLAG' packets
62 	 * - Set the FREQ packets to 'FREQ_TS' packets
63 	 * - Enable generation of output ATB traffic
64 	 */
65 
66 	val = readl_relaxed(drvdata->base + TRACE_NOC_CTRL);
67 
68 	val &= ~TRACE_NOC_CTRL_FLAGTYPE;
69 	val |= TRACE_NOC_CTRL_FREQTYPE;
70 	val |= TRACE_NOC_CTRL_PORTEN;
71 
72 	writel(val, drvdata->base + TRACE_NOC_CTRL);
73 }
74 
75 static int trace_noc_enable(struct coresight_device *csdev, struct coresight_connection *inport,
76 			    struct coresight_connection *outport)
77 {
78 	struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
79 
80 	scoped_guard(spinlock, &drvdata->spinlock) {
81 		if (csdev->refcnt == 0)
82 			trace_noc_enable_hw(drvdata);
83 
84 		csdev->refcnt++;
85 	}
86 
87 	dev_dbg(drvdata->dev, "Trace NOC is enabled\n");
88 	return 0;
89 }
90 
91 static void trace_noc_disable(struct coresight_device *csdev, struct coresight_connection *inport,
92 			      struct coresight_connection *outport)
93 {
94 	struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
95 
96 	scoped_guard(spinlock, &drvdata->spinlock) {
97 		if (--csdev->refcnt == 0)
98 			writel(0x0, drvdata->base + TRACE_NOC_CTRL);
99 	}
100 	dev_dbg(drvdata->dev, "Trace NOC is disabled\n");
101 }
102 
103 static int trace_noc_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
104 			__maybe_unused struct coresight_device *sink)
105 {
106 	struct trace_noc_drvdata *drvdata;
107 
108 	drvdata = dev_get_drvdata(csdev->dev.parent);
109 
110 	return drvdata->atid;
111 }
112 
113 static const struct coresight_ops_link trace_noc_link_ops = {
114 	.enable		= trace_noc_enable,
115 	.disable	= trace_noc_disable,
116 };
117 
118 static const struct coresight_ops trace_noc_cs_ops = {
119 	.trace_id	= trace_noc_id,
120 	.link_ops	= &trace_noc_link_ops,
121 };
122 
123 static int trace_noc_init_default_data(struct trace_noc_drvdata *drvdata)
124 {
125 	int atid;
126 
127 	atid = coresight_trace_id_get_system_id();
128 	if (atid < 0)
129 		return atid;
130 
131 	drvdata->atid = atid;
132 
133 	return 0;
134 }
135 
136 static ssize_t traceid_show(struct device *dev,
137 			    struct device_attribute *attr, char *buf)
138 {
139 	unsigned long val;
140 	struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
141 
142 	val = drvdata->atid;
143 	return sprintf(buf, "%#lx\n", val);
144 }
145 static DEVICE_ATTR_RO(traceid);
146 
147 static struct attribute *coresight_tnoc_attrs[] = {
148 	&dev_attr_traceid.attr,
149 	NULL,
150 };
151 
152 static const struct attribute_group coresight_tnoc_group = {
153 	.attrs = coresight_tnoc_attrs,
154 };
155 
156 static const struct attribute_group *coresight_tnoc_groups[] = {
157 	&coresight_tnoc_group,
158 	NULL,
159 };
160 
161 static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
162 {
163 	struct device *dev = &adev->dev;
164 	struct coresight_platform_data *pdata;
165 	struct trace_noc_drvdata *drvdata;
166 	struct coresight_desc desc = { 0 };
167 	int ret;
168 
169 	desc.name = coresight_alloc_device_name(&trace_noc_devs, dev);
170 	if (!desc.name)
171 		return -ENOMEM;
172 
173 	pdata = coresight_get_platform_data(dev);
174 	if (IS_ERR(pdata))
175 		return PTR_ERR(pdata);
176 	adev->dev.platform_data = pdata;
177 
178 	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
179 	if (!drvdata)
180 		return -ENOMEM;
181 
182 	drvdata->dev = &adev->dev;
183 	dev_set_drvdata(dev, drvdata);
184 
185 	drvdata->base = devm_ioremap_resource(dev, &adev->res);
186 	if (IS_ERR(drvdata->base))
187 		return PTR_ERR(drvdata->base);
188 
189 	spin_lock_init(&drvdata->spinlock);
190 
191 	ret = trace_noc_init_default_data(drvdata);
192 	if (ret)
193 		return ret;
194 
195 	desc.ops = &trace_noc_cs_ops;
196 	desc.type = CORESIGHT_DEV_TYPE_LINK;
197 	desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
198 	desc.pdata = adev->dev.platform_data;
199 	desc.dev = &adev->dev;
200 	desc.access = CSDEV_ACCESS_IOMEM(drvdata->base);
201 	desc.groups = coresight_tnoc_groups;
202 	drvdata->csdev = coresight_register(&desc);
203 	if (IS_ERR(drvdata->csdev)) {
204 		coresight_trace_id_put_system_id(drvdata->atid);
205 		return PTR_ERR(drvdata->csdev);
206 	}
207 	pm_runtime_put(&adev->dev);
208 
209 	return 0;
210 }
211 
212 static void trace_noc_remove(struct amba_device *adev)
213 {
214 	struct trace_noc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
215 
216 	coresight_unregister(drvdata->csdev);
217 	coresight_trace_id_put_system_id(drvdata->atid);
218 }
219 
220 static struct amba_id trace_noc_ids[] = {
221 	{
222 		.id     = 0x000f0c00,
223 		.mask   = 0x00ffff00,
224 	},
225 	{
226 		.id     = 0x001f0c00,
227 		.mask   = 0x00ffff00,
228 	},
229 	{},
230 };
231 MODULE_DEVICE_TABLE(amba, trace_noc_ids);
232 
233 static struct amba_driver trace_noc_driver = {
234 	.drv = {
235 		.name   = "coresight-trace-noc",
236 		.suppress_bind_attrs = true,
237 	},
238 	.probe          = trace_noc_probe,
239 	.remove		= trace_noc_remove,
240 	.id_table	= trace_noc_ids,
241 };
242 
243 module_amba_driver(trace_noc_driver);
244 
245 MODULE_LICENSE("GPL");
246 MODULE_DESCRIPTION("Trace NOC driver");
247