1 // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
2 /*
3 *
4 * (C) COPYRIGHT 2022 ARM Limited. All rights reserved.
5 *
6 * This program is free software and is provided to you under the terms of the
7 * GNU General Public License version 2 as published by the Free Software
8 * Foundation, and any use by you of this program is subject to the terms
9 * of such GNU license.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can access it online at
18 * http://www.gnu.org/licenses/gpl-2.0.html.
19 *
20 */
21
22 #include <linux/of_platform.h>
23 #include <coresight-priv.h>
24 #include "sources/coresight_mali_sources.h"
25
26 /* Linux Coresight framework does not support multiple sources enabled
27 * at the same time.
28 *
29 * To avoid Kernel instability, all Mali Coresight sources use the
30 * same trace ID value as the mandatory ETM one.
31 */
32 #define CS_MALI_TRACE_ID 0x00000010
33
34 #define CS_SCS_BASE_ADDR 0xE000E000
35 #define SCS_DEMCR 0xDFC
36 #define CS_ITM_BASE_ADDR 0xE0000000
37 #define ITM_TCR 0xE80
38 #define ITM_TCR_BUSY_BIT (0x1 << 22)
39 #define CS_DWT_BASE_ADDR 0xE0001000
40 #define DWT_CTRL 0x000
41 #define DWT_CYCCNT 0x004
42
43 #if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE
44 static char *type_name = "mali-source-itm";
45 #endif
46
47 #define NELEMS(s) (sizeof(s) / sizeof((s)[0]))
48
49 enum cs_itm_dwt_dynamic_regs { CS_DWT_CTRL, CS_ITM_TCR, CS_ITM_DWT_NR_DYN_REGS };
50
51 struct cs_itm_state {
52 int enabled;
53 u32 regs[CS_ITM_DWT_NR_DYN_REGS];
54 };
55
56 static struct cs_itm_state itm_state = { 0 };
57
58 static struct kbase_debug_coresight_csf_address_range dwt_itm_range[] = {
59 { CS_SCS_BASE_ADDR, CS_SCS_BASE_ADDR + CORESIGHT_DEVTYPE },
60 { CS_ITM_BASE_ADDR, CS_ITM_BASE_ADDR + CORESIGHT_DEVTYPE },
61 { CS_DWT_BASE_ADDR, CS_DWT_BASE_ADDR + CORESIGHT_DEVTYPE }
62 };
63
64 static struct kbase_debug_coresight_csf_op dwt_itm_enable_ops[] = {
65 // enable ITM/DWT functionality via DEMCR register
66 WRITE_IMM_OP(CS_SCS_BASE_ADDR + SCS_DEMCR, 0x01000000),
67 // Unlock DWT configuration
68 WRITE_IMM_OP(CS_DWT_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
69 // prep DWT counter to immediately send sync packet ((1 << 24) - 1)
70 WRITE_IMM_OP(CS_DWT_BASE_ADDR + DWT_CYCCNT, 0x00ffffff),
71 // Write initial value of post count counter
72 WRITE_IMM_OP(CS_DWT_BASE_ADDR + DWT_CTRL, 0x00000020),
73 // Set DWT configuration:
74 WRITE_PTR_OP(CS_DWT_BASE_ADDR + DWT_CTRL, &itm_state.regs[CS_DWT_CTRL]),
75 // Lock DWT Configuration
76 WRITE_IMM_OP(CS_DWT_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
77 // Unlock DWT configuration
78 WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
79 // Set ITM configuration:
80 WRITE_PTR_OP(CS_ITM_BASE_ADDR + ITM_TCR, &itm_state.regs[CS_ITM_TCR]),
81 // Lock DWT configuration
82 WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
83 // Set enabled bit on at the end of sequence
84 BIT_OR_OP(&itm_state.enabled, 0x1),
85 };
86
87 static struct kbase_debug_coresight_csf_op dwt_itm_disable_ops[] = {
88 // Disable ITM/DWT functionality via DEMCR register
89 WRITE_IMM_OP(CS_SCS_BASE_ADDR + SCS_DEMCR, 0x00000000),
90 // Unlock ITM configuration
91 WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, CS_MALI_UNLOCK_COMPONENT),
92 // Check ITM is disabled
93 POLL_OP(CS_ITM_BASE_ADDR + ITM_TCR, ITM_TCR_BUSY_BIT, 0x0),
94 // Lock
95 WRITE_IMM_OP(CS_ITM_BASE_ADDR + CORESIGHT_LAR, 0x00000000),
96 // Set enabled bit off at the end of sequence
97 BIT_AND_OP(&itm_state.enabled, 0x0),
98 };
99
set_default_regs(void)100 static void set_default_regs(void)
101 {
102 // DWT configuration:
103 // [0] = 1, enable cycle counter
104 // [4:1] = 4, set PC sample rate pf 256 cycles
105 // [8:5] = 1, set initial post count value
106 // [9] = 1, select position of post count tap on the cycle counter
107 // [10:11] = 1, enable sync packets
108 // [12] = 1, enable periodic PC sample packets
109 itm_state.regs[CS_DWT_CTRL] = 0x00001629;
110 // ITM configuration:
111 // [0] = 1, Enable ITM
112 // [1] = 1, Enable Time stamp generation
113 // [2] = 1, Enable sync packet transmission
114 // [3] = 1, Enable HW event forwarding
115 // [11:10] = 1, Generate TS request approx every 128 cycles
116 // [22:16] = 1, Trace bus ID
117 itm_state.regs[CS_ITM_TCR] = 0x0001040F;
118 }
119
verify_store_reg(struct device * dev,const char * buf,size_t count,int reg)120 static int verify_store_reg(struct device *dev, const char *buf, size_t count, int reg)
121 {
122 struct coresight_mali_source_drvdata *drvdata = dev_get_drvdata(dev->parent);
123 u32 val;
124 int err;
125
126 if (buf == NULL)
127 return -EINVAL;
128
129 if (itm_state.enabled == 1) {
130 dev_err(drvdata->base.dev,
131 "Config needs to be disabled before modifying registers\n");
132 return -EINVAL;
133 }
134
135 err = kstrtou32(buf, 0, &val);
136 if (err) {
137 dev_err(drvdata->base.dev, "Invalid input value\n");
138 return -EINVAL;
139 }
140
141 itm_state.regs[reg] = val;
142 return count;
143 }
144
is_enabled_show(struct device * dev,struct device_attribute * attr,char * const buf)145 static ssize_t is_enabled_show(struct device *dev, struct device_attribute *attr, char *const buf)
146 {
147 return sprintf(buf, "%d\n", itm_state.enabled);
148 }
149 static DEVICE_ATTR_RO(is_enabled);
150
151 #define CS_ITM_DWT_REG_ATTR_RW(_a, _b) \
152 static ssize_t _a##_show(struct device *dev, struct device_attribute *attr, \
153 char *const buf) \
154 { \
155 return sprintf(buf, "%#x\n", itm_state.regs[CS_##_b]); \
156 } \
157 static ssize_t _a##_store(struct device *dev, struct device_attribute *attr, \
158 const char *buf, size_t count) \
159 { \
160 return verify_store_reg(dev, buf, count, CS_##_b); \
161 } \
162 static DEVICE_ATTR_RW(_a)
163
164 CS_ITM_DWT_REG_ATTR_RW(dwt_ctrl, DWT_CTRL);
165 CS_ITM_DWT_REG_ATTR_RW(itm_tcr, ITM_TCR);
166
167 static struct attribute *coresight_mali_source_attrs[] = {
168 &dev_attr_is_enabled.attr,
169 &dev_attr_dwt_ctrl.attr,
170 &dev_attr_itm_tcr.attr,
171 NULL,
172 };
173
174 static const struct attribute_group coresight_mali_source_group = {
175 .attrs = coresight_mali_source_attrs,
176 .name = "mgmt"
177 };
178
179 static const struct attribute_group *coresight_mali_source_groups[] = {
180 &coresight_mali_source_group,
181 NULL,
182 };
183
coresight_mali_source_groups_get(void)184 const struct attribute_group **coresight_mali_source_groups_get(void)
185 {
186 return coresight_mali_source_groups;
187 }
188
coresight_mali_sources_init_drvdata(struct coresight_mali_source_drvdata * drvdata)189 int coresight_mali_sources_init_drvdata(struct coresight_mali_source_drvdata *drvdata)
190 {
191 if (drvdata == NULL)
192 return -EINVAL;
193
194 #if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE
195 drvdata->type_name = type_name;
196 #endif
197
198 drvdata->base.kbase_client = kbase_debug_coresight_csf_register(
199 drvdata->base.gpu_dev, dwt_itm_range, NELEMS(dwt_itm_range));
200 if (drvdata->base.kbase_client == NULL) {
201 dev_err(drvdata->base.dev, "Registration with full range failed unexpectedly\n");
202 return -EINVAL;
203 }
204
205 drvdata->trcid = CS_MALI_TRACE_ID;
206
207 drvdata->base.enable_seq.ops = dwt_itm_enable_ops;
208 drvdata->base.enable_seq.nr_ops = NELEMS(dwt_itm_enable_ops);
209
210 drvdata->base.disable_seq.ops = dwt_itm_disable_ops;
211 drvdata->base.disable_seq.nr_ops = NELEMS(dwt_itm_disable_ops);
212
213 set_default_regs();
214
215 drvdata->base.config = kbase_debug_coresight_csf_config_create(
216 drvdata->base.kbase_client, &drvdata->base.enable_seq, &drvdata->base.disable_seq);
217 if (!drvdata->base.config) {
218 dev_err(drvdata->base.dev, "config create failed unexpectedly\n");
219 kbase_debug_coresight_csf_unregister(drvdata->base.kbase_client);
220 return -EINVAL;
221 }
222
223 return 0;
224 }
225
coresight_mali_sources_deinit_drvdata(struct coresight_mali_source_drvdata * drvdata)226 void coresight_mali_sources_deinit_drvdata(struct coresight_mali_source_drvdata *drvdata)
227 {
228 if (drvdata->base.config != NULL)
229 kbase_debug_coresight_csf_config_free(drvdata->base.config);
230
231 if (drvdata->base.kbase_client != NULL)
232 kbase_debug_coresight_csf_unregister(drvdata->base.kbase_client);
233 }
234
235 static const struct of_device_id mali_source_ids[] = { { .compatible =
236 "arm,coresight-mali-source-itm" },
237 {} };
238
239 static struct platform_driver mali_sources_platform_driver = {
240 .probe = coresight_mali_sources_probe,
241 .remove = coresight_mali_sources_remove,
242 .driver = {
243 .name = "coresight-mali-source-itm",
244 .owner = THIS_MODULE,
245 .of_match_table = mali_source_ids,
246 .suppress_bind_attrs = true,
247 },
248 };
249
mali_sources_init(void)250 static int __init mali_sources_init(void)
251 {
252 return platform_driver_register(&mali_sources_platform_driver);
253 }
254
mali_sources_exit(void)255 static void __exit mali_sources_exit(void)
256 {
257 platform_driver_unregister(&mali_sources_platform_driver);
258 }
259
260 module_init(mali_sources_init);
261 module_exit(mali_sources_exit);
262
263 MODULE_AUTHOR("ARM Ltd.");
264 MODULE_DESCRIPTION("Arm Coresight Mali source ITM");
265 MODULE_LICENSE("GPL");
266