1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Bitbang driver for Linux GPIO descriptors through libgpiod
4 * Copyright (C) 2020 Antonio Borneo <borneo.antonio@gmail.com>
6 * Largely based on sysfsgpio driver
7 * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au
8 * Copyright (C) 2014 by Jean-Christian de Rivaz <jc@eclis.ch>
9 * Copyright (C) 2014 by Paul Fertser <fercerpav@gmail.com>
17 #include <jtag/adapter.h>
18 #include <jtag/interface.h>
19 #include <transport/transport.h>
22 static struct gpiod_chip
*gpiod_chip
[ADAPTER_GPIO_IDX_NUM
] = {};
23 static struct gpiod_line
*gpiod_line
[ADAPTER_GPIO_IDX_NUM
] = {};
25 static int last_swclk
;
26 static int last_swdio
;
27 static bool last_stored
;
28 static bool swdio_input
;
30 static const struct adapter_gpio_config
*adapter_gpio_config
;
33 * Helper function to determine if gpio config is valid
35 * Assume here that there will be less than 10000 gpios per gpiochip, and less
36 * than 1000 gpiochips.
38 static bool is_gpio_config_valid(enum adapter_gpio_config_index idx
)
40 return adapter_gpio_config
[idx
].chip_num
< 1000
41 && adapter_gpio_config
[idx
].gpio_num
< 10000;
44 /* Bitbang interface read of TDO */
45 static bb_value_t
linuxgpiod_read(void)
49 retval
= gpiod_line_get_value(gpiod_line
[ADAPTER_GPIO_IDX_TDO
]);
51 LOG_WARNING("reading tdo failed");
55 return retval
? BB_HIGH
: BB_LOW
;
59 * Bitbang interface write of TCK, TMS, TDI
61 * Seeing as this is the only function where the outputs are changed,
62 * we can cache the old value to avoid needlessly writing it.
64 static int linuxgpiod_write(int tck
, int tms
, int tdi
)
70 static int first_time
;
81 if (tdi
!= last_tdi
) {
82 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_TDI
], tdi
);
84 LOG_WARNING("writing tdi failed");
87 if (tms
!= last_tms
) {
88 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_TMS
], tms
);
90 LOG_WARNING("writing tms failed");
94 if (tck
!= last_tck
) {
95 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_TCK
], tck
);
97 LOG_WARNING("writing tck failed");
107 static int linuxgpiod_swdio_read(void)
111 retval
= gpiod_line_get_value(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO
]);
113 LOG_WARNING("Fail read swdio");
120 static void linuxgpiod_swdio_drive(bool is_output
)
125 * FIXME: change direction requires release and re-require the line
126 * https://stackoverflow.com/questions/58735140/
127 * this would change in future libgpiod
129 gpiod_line_release(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO
]);
132 if (gpiod_line
[ADAPTER_GPIO_IDX_SWDIO_DIR
]) {
133 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO_DIR
], 1);
135 LOG_WARNING("Fail set swdio_dir");
137 retval
= gpiod_line_request_output(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO
], "OpenOCD", 1);
139 LOG_WARNING("Fail request_output line swdio");
141 retval
= gpiod_line_request_input(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO
], "OpenOCD");
143 LOG_WARNING("Fail request_input line swdio");
144 if (gpiod_line
[ADAPTER_GPIO_IDX_SWDIO_DIR
]) {
145 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO_DIR
], 0);
147 LOG_WARNING("Fail set swdio_dir");
152 swdio_input
= !is_output
;
155 static int linuxgpiod_swd_write(int swclk
, int swdio
)
160 if (!last_stored
|| swdio
!= last_swdio
) {
161 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_SWDIO
], swdio
);
163 LOG_WARNING("Fail set swdio");
167 /* write swclk last */
168 if (!last_stored
|| swclk
!= last_swclk
) {
169 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_SWCLK
], swclk
);
171 LOG_WARNING("Fail set swclk");
181 static int linuxgpiod_blink(int on
)
185 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_LED
))
188 retval
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_LED
], on
);
190 LOG_WARNING("Fail set led");
194 static struct bitbang_interface linuxgpiod_bitbang
= {
195 .read
= linuxgpiod_read
,
196 .write
= linuxgpiod_write
,
197 .swdio_read
= linuxgpiod_swdio_read
,
198 .swdio_drive
= linuxgpiod_swdio_drive
,
199 .swd_write
= linuxgpiod_swd_write
,
200 .blink
= linuxgpiod_blink
,
204 * Bitbang interface to manipulate reset lines SRST and TRST
206 * (1) assert or (0) deassert reset lines
208 static int linuxgpiod_reset(int trst
, int srst
)
210 int retval1
= 0, retval2
= 0;
212 LOG_DEBUG("linuxgpiod_reset");
215 * active low behaviour handled by "adaptor gpio" command and
216 * GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW flag when requesting the line.
218 if (gpiod_line
[ADAPTER_GPIO_IDX_SRST
]) {
219 retval1
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_SRST
], srst
);
221 LOG_WARNING("set srst value failed");
224 if (gpiod_line
[ADAPTER_GPIO_IDX_TRST
]) {
225 retval2
= gpiod_line_set_value(gpiod_line
[ADAPTER_GPIO_IDX_TRST
], trst
);
227 LOG_WARNING("set trst value failed");
230 return ((retval1
< 0) || (retval2
< 0)) ? ERROR_FAIL
: ERROR_OK
;
233 static bool linuxgpiod_jtag_mode_possible(void)
235 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TCK
))
237 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TMS
))
239 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TDI
))
241 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TDO
))
246 static bool linuxgpiod_swd_mode_possible(void)
248 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_SWCLK
))
250 if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_SWDIO
))
255 static inline void helper_release(enum adapter_gpio_config_index idx
)
257 if (gpiod_line
[idx
]) {
258 gpiod_line_release(gpiod_line
[idx
]);
259 gpiod_line
[idx
] = NULL
;
261 if (gpiod_chip
[idx
]) {
262 gpiod_chip_close(gpiod_chip
[idx
]);
263 gpiod_chip
[idx
] = NULL
;
267 static int linuxgpiod_quit(void)
269 LOG_DEBUG("linuxgpiod_quit");
270 for (int i
= 0; i
< ADAPTER_GPIO_IDX_NUM
; ++i
)
276 static int helper_get_line(enum adapter_gpio_config_index idx
)
278 if (!is_gpio_config_valid(idx
))
281 int dir
= GPIOD_LINE_REQUEST_DIRECTION_INPUT
, flags
= 0, val
= 0, retval
;
283 gpiod_chip
[idx
] = gpiod_chip_open_by_number(adapter_gpio_config
[idx
].chip_num
);
284 if (!gpiod_chip
[idx
]) {
285 LOG_ERROR("Cannot open LinuxGPIOD chip %d for %s", adapter_gpio_config
[idx
].chip_num
,
286 adapter_gpio_get_name(idx
));
287 return ERROR_JTAG_INIT_FAILED
;
290 gpiod_line
[idx
] = gpiod_chip_get_line(gpiod_chip
[idx
], adapter_gpio_config
[idx
].gpio_num
);
291 if (!gpiod_line
[idx
]) {
292 LOG_ERROR("Error get line %s", adapter_gpio_get_name(idx
));
293 return ERROR_JTAG_INIT_FAILED
;
296 switch (adapter_gpio_config
[idx
].init_state
) {
297 case ADAPTER_GPIO_INIT_STATE_INPUT
:
298 dir
= GPIOD_LINE_REQUEST_DIRECTION_INPUT
;
300 case ADAPTER_GPIO_INIT_STATE_INACTIVE
:
301 dir
= GPIOD_LINE_REQUEST_DIRECTION_OUTPUT
;
304 case ADAPTER_GPIO_INIT_STATE_ACTIVE
:
305 dir
= GPIOD_LINE_REQUEST_DIRECTION_OUTPUT
;
310 switch (adapter_gpio_config
[idx
].drive
) {
311 case ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL
:
313 case ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN
:
314 flags
|= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN
;
316 case ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE
:
317 flags
|= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE
;
321 switch (adapter_gpio_config
[idx
].pull
) {
322 case ADAPTER_GPIO_PULL_NONE
:
323 #ifdef HAVE_LIBGPIOD1_FLAGS_BIAS
324 flags
|= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE
;
327 case ADAPTER_GPIO_PULL_UP
:
328 #ifdef HAVE_LIBGPIOD1_FLAGS_BIAS
329 flags
|= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP
;
331 LOG_WARNING("linuxgpiod: ignoring request for pull-up on %s: not supported by gpiod v%s",
332 adapter_gpio_get_name(idx
), gpiod_version_string());
335 case ADAPTER_GPIO_PULL_DOWN
:
336 #ifdef HAVE_LIBGPIOD1_FLAGS_BIAS
337 flags
|= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN
;
339 LOG_WARNING("linuxgpiod: ignoring request for pull-down on %s: not supported by gpiod v%s",
340 adapter_gpio_get_name(idx
), gpiod_version_string());
345 if (adapter_gpio_config
[idx
].active_low
)
346 flags
|= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW
;
348 struct gpiod_line_request_config config
= {
349 .consumer
= "OpenOCD",
354 retval
= gpiod_line_request(gpiod_line
[idx
], &config
, val
);
356 LOG_ERROR("Error requesting gpio line %s", adapter_gpio_get_name(idx
));
357 return ERROR_JTAG_INIT_FAILED
;
363 static int linuxgpiod_init(void)
365 LOG_INFO("Linux GPIOD JTAG/SWD bitbang driver");
367 bitbang_interface
= &linuxgpiod_bitbang
;
368 adapter_gpio_config
= adapter_gpio_get_config();
371 * Configure JTAG/SWD signals. Default directions and initial states are handled
372 * by adapter.c and "adapter gpio" command.
375 if (transport_is_jtag()) {
376 if (!linuxgpiod_jtag_mode_possible()) {
377 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
381 if (helper_get_line(ADAPTER_GPIO_IDX_TDO
) != ERROR_OK
382 || helper_get_line(ADAPTER_GPIO_IDX_TDI
) != ERROR_OK
383 || helper_get_line(ADAPTER_GPIO_IDX_TCK
) != ERROR_OK
384 || helper_get_line(ADAPTER_GPIO_IDX_TMS
) != ERROR_OK
385 || helper_get_line(ADAPTER_GPIO_IDX_TRST
) != ERROR_OK
)
389 if (transport_is_swd()) {
390 int retval1
, retval2
;
391 if (!linuxgpiod_swd_mode_possible()) {
392 LOG_ERROR("Require swclk and swdio gpio for SWD mode");
397 * swdio and its buffer should be initialized in the order that prevents
398 * two outputs from being connected together. This will occur if the
399 * swdio GPIO is configured as an output while the external buffer is
400 * configured to send the swdio signal from the target to the GPIO.
402 if (adapter_gpio_config
[ADAPTER_GPIO_IDX_SWDIO
].init_state
== ADAPTER_GPIO_INIT_STATE_INPUT
) {
403 retval1
= helper_get_line(ADAPTER_GPIO_IDX_SWDIO
);
404 retval2
= helper_get_line(ADAPTER_GPIO_IDX_SWDIO_DIR
);
406 retval1
= helper_get_line(ADAPTER_GPIO_IDX_SWDIO_DIR
);
407 retval2
= helper_get_line(ADAPTER_GPIO_IDX_SWDIO
);
409 if (retval1
!= ERROR_OK
|| retval2
!= ERROR_OK
)
412 if (helper_get_line(ADAPTER_GPIO_IDX_SWCLK
) != ERROR_OK
)
416 if (helper_get_line(ADAPTER_GPIO_IDX_SRST
) != ERROR_OK
417 || helper_get_line(ADAPTER_GPIO_IDX_LED
) != ERROR_OK
)
425 return ERROR_JTAG_INIT_FAILED
;
428 static const char *const linuxgpiod_transport
[] = { "swd", "jtag", NULL
};
430 static struct jtag_interface linuxgpiod_interface
= {
431 .supported
= DEBUG_CAP_TMS_SEQ
,
432 .execute_queue
= bitbang_execute_queue
,
435 struct adapter_driver linuxgpiod_adapter_driver
= {
436 .name
= "linuxgpiod",
437 .transports
= linuxgpiod_transport
,
439 .init
= linuxgpiod_init
,
440 .quit
= linuxgpiod_quit
,
441 .reset
= linuxgpiod_reset
,
443 .jtag_ops
= &linuxgpiod_interface
,
444 .swd_ops
= &bitbang_swd
,