adapter: switch from struct jtag_interface to adapter_driver
[openocd.git] / src / jtag / drivers / imx_gpio.c
1 /***************************************************************************
2 * Copyright (C) 2017 by Grzegorz Kostka, kostka.grzegorz@gmail.com *
3 * *
4 * Based on bcm2835gpio.c *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
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, see <http://www.gnu.org/licenses/>. *
18 ***************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <jtag/interface.h>
25 #include "bitbang.h"
26
27 #include <sys/mman.h>
28
29 #define IMX_GPIO_BASE 0x0209c000
30 #define IMX_GPIO_SIZE 0x00004000
31 #define IMX_GPIO_REGS_COUNT 8
32
33 static uint32_t imx_gpio_peri_base = IMX_GPIO_BASE;
34
35 struct imx_gpio_regs {
36 uint32_t dr;
37 uint32_t gdir;
38 uint32_t psr;
39 uint32_t icr1;
40 uint32_t icr2;
41 uint32_t imr;
42 uint32_t isr;
43 uint32_t edge_sel;
44 } __attribute__((aligned(IMX_GPIO_SIZE)));
45
46 static int dev_mem_fd;
47 static volatile struct imx_gpio_regs *pio_base;
48
49 /* GPIO setup functions */
50 static inline bool gpio_mode_get(int g)
51 {
52 return pio_base[g / 32].gdir >> (g & 0x1F) & 1;
53 }
54
55 static inline void gpio_mode_input_set(int g)
56 {
57 pio_base[g / 32].gdir &= ~(1u << (g & 0x1F));
58 }
59
60 static inline void gpio_mode_output_set(int g)
61 {
62 pio_base[g / 32].gdir |= (1u << (g & 0x1F));
63 }
64
65 static inline void gpio_mode_set(int g, int m)
66 {
67 (m) ? gpio_mode_output_set(g) : gpio_mode_input_set(g);
68 }
69
70 static inline void gpio_set(int g)
71 {
72 pio_base[g / 32].dr |= (1u << (g & 0x1F));
73 }
74
75 static inline void gpio_clear(int g)
76 {
77 pio_base[g / 32].dr &= ~(1u << (g & 0x1F));
78 }
79
80 static inline bool gpio_level(int g)
81 {
82 return pio_base[g / 32].dr >> (g & 0x1F) & 1;
83 }
84
85 static bb_value_t imx_gpio_read(void);
86 static int imx_gpio_write(int tck, int tms, int tdi);
87
88 static int imx_gpio_swdio_read(void);
89 static void imx_gpio_swdio_drive(bool is_output);
90
91 static int imx_gpio_init(void);
92 static int imx_gpio_quit(void);
93
94 static struct bitbang_interface imx_gpio_bitbang = {
95 .read = imx_gpio_read,
96 .write = imx_gpio_write,
97 .swdio_read = imx_gpio_swdio_read,
98 .swdio_drive = imx_gpio_swdio_drive,
99 .blink = NULL
100 };
101
102 /* GPIO numbers for each signal. Negative values are invalid */
103 static int tck_gpio = -1;
104 static int tck_gpio_mode;
105 static int tms_gpio = -1;
106 static int tms_gpio_mode;
107 static int tdi_gpio = -1;
108 static int tdi_gpio_mode;
109 static int tdo_gpio = -1;
110 static int tdo_gpio_mode;
111 static int trst_gpio = -1;
112 static int trst_gpio_mode;
113 static int srst_gpio = -1;
114 static int srst_gpio_mode;
115 static int swclk_gpio = -1;
116 static int swclk_gpio_mode;
117 static int swdio_gpio = -1;
118 static int swdio_gpio_mode;
119
120 /* Transition delay coefficients. Tuned for IMX6UL 528MHz. Adjusted
121 * experimentally for:10kHz, 100Khz, 500KHz. Speeds above 800Khz are impossible
122 * to reach via memory mapped method (at least for IMX6UL@528MHz).
123 * Measured mmap raw GPIO toggling speed on IMX6UL@528MHz: 1.3MHz.
124 */
125 static int speed_coeff = 50000;
126 static int speed_offset = 100;
127 static unsigned int jtag_delay;
128
129 static bb_value_t imx_gpio_read(void)
130 {
131 return gpio_level(tdo_gpio) ? BB_HIGH : BB_LOW;
132 }
133
134 static int imx_gpio_write(int tck, int tms, int tdi)
135 {
136 tms ? gpio_set(tms_gpio) : gpio_clear(tms_gpio);
137 tdi ? gpio_set(tdi_gpio) : gpio_clear(tdi_gpio);
138 tck ? gpio_set(tck_gpio) : gpio_clear(tck_gpio);
139
140 for (unsigned int i = 0; i < jtag_delay; i++)
141 asm volatile ("");
142
143 return ERROR_OK;
144 }
145
146 static int imx_gpio_swd_write(int tck, int tms, int tdi)
147 {
148 tdi ? gpio_set(swdio_gpio) : gpio_clear(swdio_gpio);
149 tck ? gpio_set(swclk_gpio) : gpio_clear(swclk_gpio);
150
151 for (unsigned int i = 0; i < jtag_delay; i++)
152 asm volatile ("");
153
154 return ERROR_OK;
155 }
156
157 /* (1) assert or (0) deassert reset lines */
158 static int imx_gpio_reset(int trst, int srst)
159 {
160 if (trst_gpio != -1)
161 trst ? gpio_clear(trst_gpio) : gpio_set(trst_gpio);
162
163 if (srst_gpio != -1)
164 srst ? gpio_clear(srst_gpio) : gpio_set(srst_gpio);
165
166 return ERROR_OK;
167 }
168
169 static void imx_gpio_swdio_drive(bool is_output)
170 {
171 if (is_output)
172 gpio_mode_output_set(swdio_gpio);
173 else
174 gpio_mode_input_set(swdio_gpio);
175 }
176
177 static int imx_gpio_swdio_read(void)
178 {
179 return gpio_level(swdio_gpio);
180 }
181
182 static int imx_gpio_khz(int khz, int *jtag_speed)
183 {
184 if (!khz) {
185 LOG_DEBUG("RCLK not supported");
186 return ERROR_FAIL;
187 }
188 *jtag_speed = speed_coeff/khz - speed_offset;
189 if (*jtag_speed < 0)
190 *jtag_speed = 0;
191 return ERROR_OK;
192 }
193
194 static int imx_gpio_speed_div(int speed, int *khz)
195 {
196 *khz = speed_coeff/(speed + speed_offset);
197 return ERROR_OK;
198 }
199
200 static int imx_gpio_speed(int speed)
201 {
202 jtag_delay = speed;
203 return ERROR_OK;
204 }
205
206 static int is_gpio_valid(int gpio)
207 {
208 return gpio >= 0 && gpio < 32 * IMX_GPIO_REGS_COUNT;
209 }
210
211 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionums)
212 {
213 if (CMD_ARGC == 4) {
214 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
215 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
216 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
217 COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
218 } else if (CMD_ARGC != 0) {
219 return ERROR_COMMAND_SYNTAX_ERROR;
220 }
221
222 command_print(CMD,
223 "imx_gpio GPIO config: tck = %d, tms = %d, tdi = %d, tdo = %d",
224 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
225
226 return ERROR_OK;
227 }
228
229 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_tck)
230 {
231 if (CMD_ARGC == 1)
232 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
233
234 command_print(CMD, "imx_gpio GPIO config: tck = %d", tck_gpio);
235 return ERROR_OK;
236 }
237
238 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_tms)
239 {
240 if (CMD_ARGC == 1)
241 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
242
243 command_print(CMD, "imx_gpio GPIO config: tms = %d", tms_gpio);
244 return ERROR_OK;
245 }
246
247 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_tdo)
248 {
249 if (CMD_ARGC == 1)
250 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
251
252 command_print(CMD, "imx_gpio GPIO config: tdo = %d", tdo_gpio);
253 return ERROR_OK;
254 }
255
256 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_tdi)
257 {
258 if (CMD_ARGC == 1)
259 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
260
261 command_print(CMD, "imx_gpio GPIO config: tdi = %d", tdi_gpio);
262 return ERROR_OK;
263 }
264
265 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_srst)
266 {
267 if (CMD_ARGC == 1)
268 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
269
270 command_print(CMD, "imx_gpio GPIO config: srst = %d", srst_gpio);
271 return ERROR_OK;
272 }
273
274 COMMAND_HANDLER(imx_gpio_handle_jtag_gpionum_trst)
275 {
276 if (CMD_ARGC == 1)
277 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
278
279 command_print(CMD, "imx_gpio GPIO config: trst = %d", trst_gpio);
280 return ERROR_OK;
281 }
282
283 COMMAND_HANDLER(imx_gpio_handle_swd_gpionums)
284 {
285 if (CMD_ARGC == 2) {
286 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
287 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
288 } else if (CMD_ARGC != 0) {
289 return ERROR_COMMAND_SYNTAX_ERROR;
290 }
291
292 command_print(CMD,
293 "imx_gpio GPIO nums: swclk = %d, swdio = %d",
294 swclk_gpio, swdio_gpio);
295
296 return ERROR_OK;
297 }
298
299 COMMAND_HANDLER(imx_gpio_handle_swd_gpionum_swclk)
300 {
301 if (CMD_ARGC == 1)
302 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
303
304 command_print(CMD, "imx_gpio num: swclk = %d", swclk_gpio);
305 return ERROR_OK;
306 }
307
308 COMMAND_HANDLER(imx_gpio_handle_swd_gpionum_swdio)
309 {
310 if (CMD_ARGC == 1)
311 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
312
313 command_print(CMD, "imx_gpio num: swdio = %d", swdio_gpio);
314 return ERROR_OK;
315 }
316
317 COMMAND_HANDLER(imx_gpio_handle_speed_coeffs)
318 {
319 if (CMD_ARGC == 2) {
320 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff);
321 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset);
322 }
323
324 command_print(CMD, "imx_gpio: speed_coeffs = %d, speed_offset = %d",
325 speed_coeff, speed_offset);
326 return ERROR_OK;
327 }
328
329 COMMAND_HANDLER(imx_gpio_handle_peripheral_base)
330 {
331 if (CMD_ARGC == 1)
332 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], imx_gpio_peri_base);
333
334 command_print(CMD, "imx_gpio: peripheral_base = 0x%08x",
335 imx_gpio_peri_base);
336 return ERROR_OK;
337 }
338
339 static const struct command_registration imx_gpio_command_handlers[] = {
340 {
341 .name = "imx_gpio_jtag_nums",
342 .handler = &imx_gpio_handle_jtag_gpionums,
343 .mode = COMMAND_CONFIG,
344 .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
345 .usage = "[tck tms tdi tdo]",
346 },
347 {
348 .name = "imx_gpio_tck_num",
349 .handler = &imx_gpio_handle_jtag_gpionum_tck,
350 .mode = COMMAND_CONFIG,
351 .help = "gpio number for tck.",
352 .usage = "[tck]",
353 },
354 {
355 .name = "imx_gpio_tms_num",
356 .handler = &imx_gpio_handle_jtag_gpionum_tms,
357 .mode = COMMAND_CONFIG,
358 .help = "gpio number for tms.",
359 .usage = "[tms]",
360 },
361 {
362 .name = "imx_gpio_tdo_num",
363 .handler = &imx_gpio_handle_jtag_gpionum_tdo,
364 .mode = COMMAND_CONFIG,
365 .help = "gpio number for tdo.",
366 .usage = "[tdo]",
367 },
368 {
369 .name = "imx_gpio_tdi_num",
370 .handler = &imx_gpio_handle_jtag_gpionum_tdi,
371 .mode = COMMAND_CONFIG,
372 .help = "gpio number for tdi.",
373 .usage = "[tdi]",
374 },
375 {
376 .name = "imx_gpio_swd_nums",
377 .handler = &imx_gpio_handle_swd_gpionums,
378 .mode = COMMAND_CONFIG,
379 .help = "gpio numbers for swclk, swdio. (in that order)",
380 .usage = "[swclk swdio]",
381 },
382 {
383 .name = "imx_gpio_swclk_num",
384 .handler = &imx_gpio_handle_swd_gpionum_swclk,
385 .mode = COMMAND_CONFIG,
386 .help = "gpio number for swclk.",
387 .usage = "[swclk]",
388 },
389 {
390 .name = "imx_gpio_swdio_num",
391 .handler = &imx_gpio_handle_swd_gpionum_swdio,
392 .mode = COMMAND_CONFIG,
393 .help = "gpio number for swdio.",
394 .usage = "[swdio]",
395 },
396 {
397 .name = "imx_gpio_srst_num",
398 .handler = &imx_gpio_handle_jtag_gpionum_srst,
399 .mode = COMMAND_CONFIG,
400 .help = "gpio number for srst.",
401 .usage = "[srst]",
402 },
403 {
404 .name = "imx_gpio_trst_num",
405 .handler = &imx_gpio_handle_jtag_gpionum_trst,
406 .mode = COMMAND_CONFIG,
407 .help = "gpio number for trst.",
408 .usage = "[trst]",
409 },
410 {
411 .name = "imx_gpio_speed_coeffs",
412 .handler = &imx_gpio_handle_speed_coeffs,
413 .mode = COMMAND_CONFIG,
414 .help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.",
415 .usage = "[SPEED_COEFF SPEED_OFFSET]",
416 },
417 {
418 .name = "imx_gpio_peripheral_base",
419 .handler = &imx_gpio_handle_peripheral_base,
420 .mode = COMMAND_CONFIG,
421 .help = "peripheral base to access GPIOs (0x0209c000 for most IMX).",
422 .usage = "[base]",
423 },
424
425 COMMAND_REGISTRATION_DONE
426 };
427
428 static const char * const imx_gpio_transports[] = { "jtag", "swd", NULL };
429
430 static struct jtag_interface imx_gpio_interface = {
431 .supported = DEBUG_CAP_TMS_SEQ,
432 .execute_queue = bitbang_execute_queue,
433 };
434
435 struct adapter_driver imx_gpio_adapter_driver = {
436 .name = "imx_gpio",
437 .transports = imx_gpio_transports,
438 .commands = imx_gpio_command_handlers,
439
440 .init = imx_gpio_init,
441 .quit = imx_gpio_quit,
442 .reset = imx_gpio_reset,
443 .speed = imx_gpio_speed,
444 .khz = imx_gpio_khz,
445 .speed_div = imx_gpio_speed_div,
446
447 .jtag_ops = &imx_gpio_interface,
448 .swd_ops = &bitbang_swd,
449 };
450
451 static bool imx_gpio_jtag_mode_possible(void)
452 {
453 if (!is_gpio_valid(tck_gpio))
454 return 0;
455 if (!is_gpio_valid(tms_gpio))
456 return 0;
457 if (!is_gpio_valid(tdi_gpio))
458 return 0;
459 if (!is_gpio_valid(tdo_gpio))
460 return 0;
461 return 1;
462 }
463
464 static bool imx_gpio_swd_mode_possible(void)
465 {
466 if (!is_gpio_valid(swclk_gpio))
467 return 0;
468 if (!is_gpio_valid(swdio_gpio))
469 return 0;
470 return 1;
471 }
472
473 static int imx_gpio_init(void)
474 {
475 bitbang_interface = &imx_gpio_bitbang;
476
477 LOG_INFO("imx_gpio GPIO JTAG/SWD bitbang driver");
478
479 if (imx_gpio_jtag_mode_possible()) {
480 if (imx_gpio_swd_mode_possible())
481 LOG_INFO("JTAG and SWD modes enabled");
482 else
483 LOG_INFO("JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)");
484 } else if (imx_gpio_swd_mode_possible()) {
485 LOG_INFO("SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)");
486 } else {
487 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode and/or swclk and swdio gpio for SWD mode");
488 return ERROR_JTAG_INIT_FAILED;
489 }
490
491 dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
492 if (dev_mem_fd < 0) {
493 perror("open");
494 return ERROR_JTAG_INIT_FAILED;
495 }
496
497
498 LOG_INFO("imx_gpio mmap: pagesize: %u, regionsize: %u",
499 (unsigned int) sysconf(_SC_PAGE_SIZE), IMX_GPIO_REGS_COUNT * IMX_GPIO_SIZE);
500 pio_base = mmap(NULL, IMX_GPIO_REGS_COUNT * IMX_GPIO_SIZE,
501 PROT_READ | PROT_WRITE,
502 MAP_SHARED, dev_mem_fd, imx_gpio_peri_base);
503
504 if (pio_base == MAP_FAILED) {
505 perror("mmap");
506 close(dev_mem_fd);
507 return ERROR_JTAG_INIT_FAILED;
508 }
509
510 /*
511 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
512 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
513 */
514 if (imx_gpio_jtag_mode_possible()) {
515 tdo_gpio_mode = gpio_mode_get(tdo_gpio);
516 tdi_gpio_mode = gpio_mode_get(tdi_gpio);
517 tck_gpio_mode = gpio_mode_get(tck_gpio);
518 tms_gpio_mode = gpio_mode_get(tms_gpio);
519
520 gpio_clear(tdi_gpio);
521 gpio_clear(tck_gpio);
522 gpio_set(tms_gpio);
523
524 gpio_mode_input_set(tdo_gpio);
525 gpio_mode_output_set(tdi_gpio);
526 gpio_mode_output_set(tck_gpio);
527 gpio_mode_output_set(tms_gpio);
528 }
529 if (imx_gpio_swd_mode_possible()) {
530 swclk_gpio_mode = gpio_mode_get(swclk_gpio);
531 swdio_gpio_mode = gpio_mode_get(swdio_gpio);
532
533 gpio_clear(swdio_gpio);
534 gpio_clear(swclk_gpio);
535 gpio_mode_output_set(swclk_gpio);
536 gpio_mode_output_set(swdio_gpio);
537 }
538 if (trst_gpio != -1) {
539 trst_gpio_mode = gpio_mode_get(trst_gpio);
540 gpio_set(trst_gpio);
541 gpio_mode_output_set(trst_gpio);
542 }
543 if (srst_gpio != -1) {
544 srst_gpio_mode = gpio_mode_get(srst_gpio);
545 gpio_set(srst_gpio);
546 gpio_mode_output_set(srst_gpio);
547 }
548
549 LOG_DEBUG("saved pinmux settings: tck %d tms %d tdi %d "
550 "tdo %d trst %d srst %d", tck_gpio_mode, tms_gpio_mode,
551 tdi_gpio_mode, tdo_gpio_mode, trst_gpio_mode, srst_gpio_mode);
552
553 if (swd_mode) {
554 imx_gpio_bitbang.write = imx_gpio_swd_write;
555 bitbang_switch_to_swd();
556 }
557
558 return ERROR_OK;
559 }
560
561 static int imx_gpio_quit(void)
562 {
563 if (imx_gpio_jtag_mode_possible()) {
564 gpio_mode_set(tdo_gpio, tdo_gpio_mode);
565 gpio_mode_set(tdi_gpio, tdi_gpio_mode);
566 gpio_mode_set(tck_gpio, tck_gpio_mode);
567 gpio_mode_set(tms_gpio, tms_gpio_mode);
568 }
569 if (imx_gpio_swd_mode_possible()) {
570 gpio_mode_set(swclk_gpio, swclk_gpio_mode);
571 gpio_mode_set(swdio_gpio, swdio_gpio_mode);
572 }
573 if (trst_gpio != -1)
574 gpio_mode_set(trst_gpio, trst_gpio_mode);
575 if (srst_gpio != -1)
576 gpio_mode_set(srst_gpio, srst_gpio_mode);
577
578 return ERROR_OK;
579 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)