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