armv7m: add generic trace support (TPIU, ITM, etc.)
[fw/openocd] / src / target / armv7m_trace.c
1 /***************************************************************************
2  *   Copyright (C) 2015  Paul Fertser <fercerpav@gmail.com>                *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  ***************************************************************************/
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include <target/target.h>
20 #include <target/armv7m.h>
21 #include <target/cortex_m.h>
22 #include <target/armv7m_trace.h>
23
24 int armv7m_trace_tpiu_config(struct target *target)
25 {
26         struct armv7m_common *armv7m = target_to_armv7m(target);
27         struct armv7m_trace_config *trace_config = &armv7m->trace_config;
28         int prescaler;
29         int retval;
30
31         if (!trace_config->trace_freq) {
32                 LOG_ERROR("Trace port frequency is 0, can't enable TPIU");
33                 return ERROR_FAIL;
34         }
35
36         if (trace_config->traceclkin_freq % trace_config->trace_freq) {
37                 LOG_ERROR("Can not calculate an integer divisor to get %u trace port frequency from %u TRACECLKIN frequency",
38                           trace_config->trace_freq, trace_config->traceclkin_freq);
39                 return ERROR_FAIL;
40         }
41
42         prescaler = trace_config->traceclkin_freq / trace_config->trace_freq;
43
44         retval = target_write_u32(target, TPIU_CSPSR, 1 << trace_config->port_size);
45         if (retval != ERROR_OK)
46                 return retval;
47
48         retval = target_write_u32(target, TPIU_ACPR, prescaler - 1);
49         if (retval != ERROR_OK)
50                 return retval;
51
52         retval = target_write_u32(target, TPIU_SPPR, trace_config->pin_protocol);
53         if (retval != ERROR_OK)
54                 return retval;
55
56         uint32_t ffcr;
57         retval = target_read_u32(target, TPIU_FFCR, &ffcr);
58         if (retval != ERROR_OK)
59                 return retval;
60         if (trace_config->formatter)
61                 ffcr |= (1 << 1);
62         else
63                 ffcr &= ~(1 << 1);
64         retval = target_write_u32(target, TPIU_FFCR, ffcr);
65         if (retval != ERROR_OK)
66                 return retval;
67
68         target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
69
70         return ERROR_OK;
71 }
72
73 int armv7m_trace_itm_config(struct target *target)
74 {
75         struct armv7m_common *armv7m = target_to_armv7m(target);
76         struct armv7m_trace_config *trace_config = &armv7m->trace_config;
77         int retval;
78
79         retval = target_write_u32(target, ITM_LAR, ITM_LAR_KEY);
80         if (retval != ERROR_OK)
81                 return retval;
82
83         /* Enable ITM, TXENA, set TraceBusID and other parameters */
84         retval = target_write_u32(target, ITM_TCR, (1 << 0) | (1 << 3) |
85                                   (trace_config->itm_diff_timestamps << 1) |
86                                   (trace_config->itm_synchro_packets << 2) |
87                                   (trace_config->itm_async_timestamps << 4) |
88                                   (trace_config->itm_ts_prescale << 8) |
89                                   (trace_config->trace_bus_id << 16));
90         if (retval != ERROR_OK)
91                 return retval;
92
93         for (unsigned int i = 0; i < 8; i++) {
94                 retval = target_write_u32(target, ITM_TER0 + i * 4,
95                                           trace_config->itm_ter[i]);
96                 if (retval != ERROR_OK)
97                         return retval;
98         }
99
100         return ERROR_OK;
101 }
102
103 static void close_trace_file(struct armv7m_common *armv7m)
104 {
105         if (armv7m->trace_config.trace_file)
106                 fclose(armv7m->trace_config.trace_file);
107         armv7m->trace_config.trace_file = NULL;
108 }
109
110 COMMAND_HANDLER(handle_tpiu_config_command)
111 {
112         struct target *target = get_current_target(CMD_CTX);
113         struct armv7m_common *armv7m = target_to_armv7m(target);
114
115         unsigned int cmd_idx = 0;
116
117         if (CMD_ARGC == cmd_idx)
118                 return ERROR_COMMAND_SYNTAX_ERROR;
119         if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
120                 if (CMD_ARGC == cmd_idx + 1) {
121                         close_trace_file(armv7m);
122
123                         armv7m->trace_config.config_type = DISABLED;
124                         if (CMD_CTX->mode == COMMAND_EXEC)
125                                 return armv7m_trace_tpiu_config(target);
126                         else
127                                 return ERROR_OK;
128                 }
129         } else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
130                    !strcmp(CMD_ARGV[cmd_idx], "internal")) {
131                 close_trace_file(armv7m);
132
133                 armv7m->trace_config.config_type = EXTERNAL;
134                 if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
135                         cmd_idx++;
136                         if (CMD_ARGC == cmd_idx)
137                                 return ERROR_COMMAND_SYNTAX_ERROR;
138
139                         armv7m->trace_config.config_type = INTERNAL;
140                         armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
141                         if (!armv7m->trace_config.trace_file) {
142                                 LOG_ERROR("Can't open trace destination file");
143                                 return ERROR_FAIL;
144                         }
145                 }
146                 cmd_idx++;
147                 if (CMD_ARGC == cmd_idx)
148                         return ERROR_COMMAND_SYNTAX_ERROR;
149
150                 if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
151                         armv7m->trace_config.pin_protocol = SYNC;
152
153                         cmd_idx++;
154                         if (CMD_ARGC == cmd_idx)
155                                 return ERROR_COMMAND_SYNTAX_ERROR;
156
157                         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
158                 } else {
159                         if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
160                                 armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER;
161                         else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
162                                 armv7m->trace_config.pin_protocol = ASYNC_UART;
163                         else
164                                 return ERROR_COMMAND_SYNTAX_ERROR;
165
166                         cmd_idx++;
167                         if (CMD_ARGC == cmd_idx)
168                                 return ERROR_COMMAND_SYNTAX_ERROR;
169
170                         COMMAND_PARSE_ON_OFF(CMD_ARGV[cmd_idx], armv7m->trace_config.formatter);
171                 }
172
173                 cmd_idx++;
174                 if (CMD_ARGC == cmd_idx)
175                         return ERROR_COMMAND_SYNTAX_ERROR;
176
177                 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.traceclkin_freq);
178
179                 cmd_idx++;
180                 if (CMD_ARGC != cmd_idx) {
181                         COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
182                         cmd_idx++;
183                 } else {
184                         if (armv7m->trace_config.config_type != INTERNAL) {
185                                 LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
186                                 return ERROR_COMMAND_SYNTAX_ERROR;
187                         }
188                         armv7m->trace_config.trace_freq = 0;
189                 }
190
191                 if (CMD_ARGC == cmd_idx) {
192                         if (CMD_CTX->mode == COMMAND_EXEC)
193                                 return armv7m_trace_tpiu_config(target);
194                         else
195                                 return ERROR_OK;
196                 }
197         }
198
199         return ERROR_COMMAND_SYNTAX_ERROR;
200 }
201
202 COMMAND_HANDLER(handle_itm_port_command)
203 {
204         struct target *target = get_current_target(CMD_CTX);
205         struct armv7m_common *armv7m = target_to_armv7m(target);
206         unsigned int reg_idx;
207         uint8_t port;
208         bool enable;
209
210         if (CMD_ARGC != 2)
211                 return ERROR_COMMAND_SYNTAX_ERROR;
212
213         COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], port);
214         COMMAND_PARSE_ON_OFF(CMD_ARGV[1], enable);
215         reg_idx = port / 32;
216         port = port % 32;
217         if (enable)
218                 armv7m->trace_config.itm_ter[reg_idx] |= (1 << port);
219         else
220                 armv7m->trace_config.itm_ter[reg_idx] &= ~(1 << port);
221
222         if (CMD_CTX->mode == COMMAND_EXEC)
223                 return armv7m_trace_itm_config(target);
224         else
225                 return ERROR_OK;
226 }
227
228 COMMAND_HANDLER(handle_itm_ports_command)
229 {
230         struct target *target = get_current_target(CMD_CTX);
231         struct armv7m_common *armv7m = target_to_armv7m(target);
232         bool enable;
233
234         if (CMD_ARGC != 1)
235                 return ERROR_COMMAND_SYNTAX_ERROR;
236
237         COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
238         memset(armv7m->trace_config.itm_ter, enable ? 0xff : 0,
239                sizeof(armv7m->trace_config.itm_ter));
240
241         if (CMD_CTX->mode == COMMAND_EXEC)
242                 return armv7m_trace_itm_config(target);
243         else
244                 return ERROR_OK;
245 }
246
247 static const struct command_registration tpiu_command_handlers[] = {
248         {
249                 .name = "config",
250                 .handler = handle_tpiu_config_command,
251                 .mode = COMMAND_ANY,
252                 .help = "Configure TPIU features",
253                 .usage = "(disable | "
254                 "((external | internal <filename>) "
255                 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
256                 "<TRACECLKIN freq> [<trace freq>]))",
257         },
258         COMMAND_REGISTRATION_DONE
259 };
260
261 static const struct command_registration itm_command_handlers[] = {
262         {
263                 .name = "port",
264                 .handler = handle_itm_port_command,
265                 .mode = COMMAND_ANY,
266                 .help = "Enable or disable ITM stimulus port",
267                 .usage = "<port> (0|1|on|off)",
268         },
269         {
270                 .name = "ports",
271                 .handler = handle_itm_ports_command,
272                 .mode = COMMAND_ANY,
273                 .help = "Enable or disable all ITM stimulus ports",
274                 .usage = "(0|1|on|off)",
275         },
276         COMMAND_REGISTRATION_DONE
277 };
278
279 const struct command_registration armv7m_trace_command_handlers[] = {
280         {
281                 .name = "tpiu",
282                 .mode = COMMAND_ANY,
283                 .help = "tpiu command group",
284                 .usage = "",
285                 .chain = tpiu_command_handlers,
286         },
287         {
288                 .name = "itm",
289                 .mode = COMMAND_ANY,
290                 .help = "itm command group",
291                 .usage = "",
292                 .chain = itm_command_handlers,
293         },
294         COMMAND_REGISTRATION_DONE
295 };