hc
2024-03-26 e0728245c89800c2038c23308f2d88969d5b41c8
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
 *
 * (C) COPYRIGHT 2022 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 *
 */
 
#include <linux/of_platform.h>
#include <coresight-priv.h>
#include "sources/coresight_mali_sources.h"
 
/* Linux Coresight framework does not support multiple sources enabled
 * at the same time.
 *
 * To avoid Kernel instability, all Mali Coresight sources use the
 * same trace ID value as the mandatory ETM one.
 */
#define CS_MALI_TRACE_ID 0x00000010
 
#define CS_SCS_BASE_ADDR 0xE000E000
#define SCS_DEMCR 0xDFC
#define CS_ITM_BASE_ADDR 0xE0000000
#define ITM_TCR 0xE80
#define ITM_TCR_BUSY_BIT (0x1 << 22)
#define CS_DWT_BASE_ADDR 0xE0001000
#define DWT_CTRL 0x000
#define DWT_CYCCNT 0x004
 
#if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE
static char *type_name = "mali-source-itm";
#endif
 
#define NELEMS(s) (sizeof(s) / sizeof((s)[0]))
 
enum cs_itm_dwt_dynamic_regs { CS_DWT_CTRL, CS_ITM_TCR, CS_ITM_DWT_NR_DYN_REGS };
 
struct cs_itm_state {
   int enabled;
   u32 regs[CS_ITM_DWT_NR_DYN_REGS];
};
 
static struct cs_itm_state itm_state = { 0 };
 
static struct kbase_debug_coresight_csf_address_range dwt_itm_range[] = {
   { CS_SCS_BASE_ADDR, CS_SCS_BASE_ADDR + CORESIGHT_DEVTYPE },
   { CS_ITM_BASE_ADDR, CS_ITM_BASE_ADDR + CORESIGHT_DEVTYPE },
   { CS_DWT_BASE_ADDR, CS_DWT_BASE_ADDR + CORESIGHT_DEVTYPE }
};
 
static struct kbase_debug_coresight_csf_op dwt_itm_enable_ops[] = {
   // enable ITM/DWT functionality via DEMCR register
   WRITE_IMM_OP(CS_SCS_BASE_ADDR + SCS_DEMCR, 0x01000000),
   // Unlock DWT configuration
   WRITE_IMM_OP(CS_DWT_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
   // prep DWT counter to immediately send sync packet ((1 << 24) - 1)
   WRITE_IMM_OP(CS_DWT_BASE_ADDR + DWT_CYCCNT, 0x00ffffff),
   // Write initial value of post count counter
   WRITE_IMM_OP(CS_DWT_BASE_ADDR + DWT_CTRL, 0x00000020),
   // Set DWT configuration:
   WRITE_PTR_OP(CS_DWT_BASE_ADDR + DWT_CTRL, &itm_state.regs[CS_DWT_CTRL]),
   // Lock DWT Configuration
   WRITE_IMM_OP(CS_DWT_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
   // Unlock DWT configuration
   WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
   // Set ITM configuration:
   WRITE_PTR_OP(CS_ITM_BASE_ADDR + ITM_TCR, &itm_state.regs[CS_ITM_TCR]),
   // Lock DWT configuration
   WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
   // Set enabled bit on at the end of sequence
   BIT_OR_OP(&itm_state.enabled, 0x1),
};
 
static struct kbase_debug_coresight_csf_op dwt_itm_disable_ops[] = {
   // Disable ITM/DWT functionality via DEMCR register
   WRITE_IMM_OP(CS_SCS_BASE_ADDR + SCS_DEMCR, 0x00000000),
   // Unlock ITM configuration
   WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
   // Check ITM is disabled
   POLL_OP(CS_ITM_BASE_ADDR + ITM_TCR, ITM_TCR_BUSY_BIT, 0x0),
   // Lock
   WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
   // Set enabled bit off at the end of sequence
   BIT_AND_OP(&itm_state.enabled, 0x0),
};
 
static void set_default_regs(void)
{
   // DWT configuration:
   // [0] = 1, enable cycle counter
   // [4:1] = 4, set PC sample rate pf 256 cycles
   // [8:5] = 1, set initial post count value
   // [9] = 1, select position of post count tap on the cycle counter
   // [10:11] = 1, enable sync packets
   // [12] = 1, enable periodic PC sample packets
   itm_state.regs[CS_DWT_CTRL] = 0x00001629;
   // ITM configuration:
   // [0] = 1, Enable ITM
   // [1] = 1, Enable Time stamp generation
   // [2] = 1, Enable sync packet transmission
   // [3] = 1, Enable HW event forwarding
   // [11:10] = 1, Generate TS request approx every 128 cycles
   // [22:16] = 1, Trace bus ID
   itm_state.regs[CS_ITM_TCR] = 0x0001040F;
}
 
static int verify_store_reg(struct device *dev, const char *buf, size_t count, int reg)
{
   struct coresight_mali_source_drvdata *drvdata = dev_get_drvdata(dev->parent);
   u32 val;
   int err;
 
   if (buf == NULL)
       return -EINVAL;
 
   if (itm_state.enabled == 1) {
       dev_err(drvdata->base.dev,
           "Config needs to be disabled before modifying registers\n");
       return -EINVAL;
   }
 
   err = kstrtou32(buf, 0, &val);
   if (err) {
       dev_err(drvdata->base.dev, "Invalid input value\n");
       return -EINVAL;
   }
 
   itm_state.regs[reg] = val;
   return count;
}
 
static ssize_t is_enabled_show(struct device *dev, struct device_attribute *attr, char *const buf)
{
   return sprintf(buf, "%d\n", itm_state.enabled);
}
static DEVICE_ATTR_RO(is_enabled);
 
#define CS_ITM_DWT_REG_ATTR_RW(_a, _b)                                                             \
   static ssize_t _a##_show(struct device *dev, struct device_attribute *attr,                \
                char *const buf)                                                  \
   {                                                                                          \
       return sprintf(buf, "%#x\n", itm_state.regs[CS_##_b]);                             \
   }                                                                                          \
   static ssize_t _a##_store(struct device *dev, struct device_attribute *attr,               \
                 const char *buf, size_t count)                                   \
   {                                                                                          \
       return verify_store_reg(dev, buf, count, CS_##_b);                                 \
   }                                                                                          \
   static DEVICE_ATTR_RW(_a)
 
CS_ITM_DWT_REG_ATTR_RW(dwt_ctrl, DWT_CTRL);
CS_ITM_DWT_REG_ATTR_RW(itm_tcr, ITM_TCR);
 
static struct attribute *coresight_mali_source_attrs[] = {
   &dev_attr_is_enabled.attr,
   &dev_attr_dwt_ctrl.attr,
   &dev_attr_itm_tcr.attr,
   NULL,
};
 
static const struct attribute_group coresight_mali_source_group = {
   .attrs = coresight_mali_source_attrs,
   .name = "mgmt"
};
 
static const struct attribute_group *coresight_mali_source_groups[] = {
   &coresight_mali_source_group,
   NULL,
};
 
const struct attribute_group **coresight_mali_source_groups_get(void)
{
   return coresight_mali_source_groups;
}
 
int coresight_mali_sources_init_drvdata(struct coresight_mali_source_drvdata *drvdata)
{
   if (drvdata == NULL)
       return -EINVAL;
 
#if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE
   drvdata->type_name = type_name;
#endif
 
   drvdata->base.kbase_client = kbase_debug_coresight_csf_register(
       drvdata->base.gpu_dev, dwt_itm_range, NELEMS(dwt_itm_range));
   if (drvdata->base.kbase_client == NULL) {
       dev_err(drvdata->base.dev, "Registration with full range failed unexpectedly\n");
       return -EINVAL;
   }
 
   drvdata->trcid = CS_MALI_TRACE_ID;
 
   drvdata->base.enable_seq.ops = dwt_itm_enable_ops;
   drvdata->base.enable_seq.nr_ops = NELEMS(dwt_itm_enable_ops);
 
   drvdata->base.disable_seq.ops = dwt_itm_disable_ops;
   drvdata->base.disable_seq.nr_ops = NELEMS(dwt_itm_disable_ops);
 
   set_default_regs();
 
   drvdata->base.config = kbase_debug_coresight_csf_config_create(
       drvdata->base.kbase_client, &drvdata->base.enable_seq, &drvdata->base.disable_seq);
   if (!drvdata->base.config) {
       dev_err(drvdata->base.dev, "config create failed unexpectedly\n");
       kbase_debug_coresight_csf_unregister(drvdata->base.kbase_client);
       return -EINVAL;
   }
 
   return 0;
}
 
void coresight_mali_sources_deinit_drvdata(struct coresight_mali_source_drvdata *drvdata)
{
   if (drvdata->base.config != NULL)
       kbase_debug_coresight_csf_config_free(drvdata->base.config);
 
   if (drvdata->base.kbase_client != NULL)
       kbase_debug_coresight_csf_unregister(drvdata->base.kbase_client);
}
 
static const struct of_device_id mali_source_ids[] = { { .compatible =
                                "arm,coresight-mali-source-itm" },
                              {} };
 
static struct platform_driver mali_sources_platform_driver = {
   .probe      = coresight_mali_sources_probe,
   .remove     = coresight_mali_sources_remove,
   .driver = {
       .name = "coresight-mali-source-itm",
       .owner = THIS_MODULE,
       .of_match_table = mali_source_ids,
       .suppress_bind_attrs    = true,
   },
};
 
static int __init mali_sources_init(void)
{
   return platform_driver_register(&mali_sources_platform_driver);
}
 
static void __exit mali_sources_exit(void)
{
   platform_driver_unregister(&mali_sources_platform_driver);
}
 
module_init(mali_sources_init);
module_exit(mali_sources_exit);
 
MODULE_AUTHOR("ARM Ltd.");
MODULE_DESCRIPTION("Arm Coresight Mali source ITM");
MODULE_LICENSE("GPL");