This is an automated email from Gerrit. "Michael Heimpold <m...@heimpold.de>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8183
-- gerrit commit ed966dbafbc29b0324497f6643f074f5615cb818 Author: Michael Heimpold <m...@heimpold.de> Date: Wed Mar 20 21:36:07 2024 +0100 drivers/linuxgpiod: restore duplicated linuxgpiod.c Change-Id: Ia75e18ecd539fe5186e0498b8adb5cdeb4580886 Signed-off-by: Michael Heimpold <m...@heimpold.de> diff --git a/src/jtag/drivers/linuxgpiod.c b/src/jtag/drivers/linuxgpiod.c new file mode 100644 index 0000000000..9428837883 --- /dev/null +++ b/src/jtag/drivers/linuxgpiod.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Bitbang driver for Linux GPIO descriptors through libgpiod + * Copyright (C) 2020 Antonio Borneo <borneo.anto...@gmail.com> + * + * Largely based on sysfsgpio driver + * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au + * Copyright (C) 2014 by Jean-Christian de Rivaz <j...@eclis.ch> + * Copyright (C) 2014 by Paul Fertser <fercer...@gmail.com> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gpiod.h> +#include <jtag/adapter.h> +#include <jtag/interface.h> +#include <transport/transport.h> +#include "bitbang.h" + +static struct gpiod_chip *gpiod_chip[ADAPTER_GPIO_IDX_NUM] = {}; +static struct gpiod_line *gpiod_line[ADAPTER_GPIO_IDX_NUM] = {}; + +static int last_swclk; +static int last_swdio; +static bool last_stored; +static bool swdio_input; + +static const struct adapter_gpio_config *adapter_gpio_config; + +/* + * Helper function to determine if gpio config is valid + * + * Assume here that there will be less than 10000 gpios per gpiochip, and less + * than 1000 gpiochips. + */ +static bool is_gpio_config_valid(enum adapter_gpio_config_index idx) +{ + return adapter_gpio_config[idx].chip_num < 1000 + && adapter_gpio_config[idx].gpio_num < 10000; +} + +/* Bitbang interface read of TDO */ +static bb_value_t linuxgpiod_read(void) +{ + int retval; + + retval = gpiod_line_get_value(gpiod_line[ADAPTER_GPIO_IDX_TDO]); + if (retval < 0) { + LOG_WARNING("reading tdo failed"); + return 0; + } + + return retval ? BB_HIGH : BB_LOW; +} + +/* + * Bitbang interface write of TCK, TMS, TDI + * + * Seeing as this is the only function where the outputs are changed, + * we can cache the old value to avoid needlessly writing it. + */ +static int linuxgpiod_write(int tck, int tms, int tdi) +{ + static int last_tck; + static int last_tms; + static int last_tdi; + + static int first_time; + + int retval; + + if (!first_time) { + last_tck = !tck; + last_tms = !tms; + last_tdi = !tdi; + first_time = 1; + } + + if (tdi != last_tdi) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_TDI], tdi); + if (retval < 0) + LOG_WARNING("writing tdi failed"); + } + + if (tms != last_tms) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_TMS], tms); + if (retval < 0) + LOG_WARNING("writing tms failed"); + } + + /* write clk last */ + if (tck != last_tck) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_TCK], tck); + if (retval < 0) + LOG_WARNING("writing tck failed"); + } + + last_tdi = tdi; + last_tms = tms; + last_tck = tck; + + return ERROR_OK; +} + +static int linuxgpiod_swdio_read(void) +{ + int retval; + + retval = gpiod_line_get_value(gpiod_line[ADAPTER_GPIO_IDX_SWDIO]); + if (retval < 0) { + LOG_WARNING("Fail read swdio"); + return 0; + } + + return retval; +} + +static void linuxgpiod_swdio_drive(bool is_output) +{ + int retval; + + /* + * FIXME: change direction requires release and re-require the line + * https://stackoverflow.com/questions/58735140/ + * this would change in future libgpiod + */ + gpiod_line_release(gpiod_line[ADAPTER_GPIO_IDX_SWDIO]); + + if (is_output) { + if (gpiod_line[ADAPTER_GPIO_IDX_SWDIO_DIR]) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_SWDIO_DIR], 1); + if (retval < 0) + LOG_WARNING("Fail set swdio_dir"); + } + retval = gpiod_line_request_output(gpiod_line[ADAPTER_GPIO_IDX_SWDIO], "OpenOCD", 1); + if (retval < 0) + LOG_WARNING("Fail request_output line swdio"); + } else { + retval = gpiod_line_request_input(gpiod_line[ADAPTER_GPIO_IDX_SWDIO], "OpenOCD"); + if (retval < 0) + LOG_WARNING("Fail request_input line swdio"); + if (gpiod_line[ADAPTER_GPIO_IDX_SWDIO_DIR]) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_SWDIO_DIR], 0); + if (retval < 0) + LOG_WARNING("Fail set swdio_dir"); + } + } + + last_stored = false; + swdio_input = !is_output; +} + +static int linuxgpiod_swd_write(int swclk, int swdio) +{ + int retval; + + if (!swdio_input) { + if (!last_stored || (swdio != last_swdio)) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_SWDIO], swdio); + if (retval < 0) + LOG_WARNING("Fail set swdio"); + } + } + + /* write swclk last */ + if (!last_stored || (swclk != last_swclk)) { + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_SWCLK], swclk); + if (retval < 0) + LOG_WARNING("Fail set swclk"); + } + + last_swdio = swdio; + last_swclk = swclk; + last_stored = true; + + return ERROR_OK; +} + +static int linuxgpiod_blink(int on) +{ + int retval; + + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_LED)) + return ERROR_OK; + + retval = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_LED], on); + if (retval < 0) + LOG_WARNING("Fail set led"); + return retval; +} + +static struct bitbang_interface linuxgpiod_bitbang = { + .read = linuxgpiod_read, + .write = linuxgpiod_write, + .swdio_read = linuxgpiod_swdio_read, + .swdio_drive = linuxgpiod_swdio_drive, + .swd_write = linuxgpiod_swd_write, + .blink = linuxgpiod_blink, +}; + +/* + * Bitbang interface to manipulate reset lines SRST and TRST + * + * (1) assert or (0) deassert reset lines + */ +static int linuxgpiod_reset(int trst, int srst) +{ + int retval1 = 0, retval2 = 0; + + LOG_DEBUG("linuxgpiod_reset"); + + /* + * active low behaviour handled by "adaptor gpio" command and + * GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW flag when requesting the line. + */ + if (gpiod_line[ADAPTER_GPIO_IDX_SRST]) { + retval1 = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_SRST], srst); + if (retval1 < 0) + LOG_WARNING("set srst value failed"); + } + + if (gpiod_line[ADAPTER_GPIO_IDX_TRST]) { + retval2 = gpiod_line_set_value(gpiod_line[ADAPTER_GPIO_IDX_TRST], trst); + if (retval2 < 0) + LOG_WARNING("set trst value failed"); + } + + return ((retval1 < 0) || (retval2 < 0)) ? ERROR_FAIL : ERROR_OK; +} + +static bool linuxgpiod_jtag_mode_possible(void) +{ + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TCK)) + return false; + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TMS)) + return false; + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TDI)) + return false; + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_TDO)) + return false; + return true; +} + +static bool linuxgpiod_swd_mode_possible(void) +{ + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_SWCLK)) + return false; + if (!is_gpio_config_valid(ADAPTER_GPIO_IDX_SWDIO)) + return false; + return true; +} + +static inline void helper_release(enum adapter_gpio_config_index idx) +{ + if (gpiod_line[idx]) { + gpiod_line_release(gpiod_line[idx]); + gpiod_line[idx] = NULL; + } + if (gpiod_chip[idx]) { + gpiod_chip_close(gpiod_chip[idx]); + gpiod_chip[idx] = NULL; + } +} + +static int linuxgpiod_quit(void) +{ + LOG_DEBUG("linuxgpiod_quit"); + for (int i = 0; i < ADAPTER_GPIO_IDX_NUM; ++i) + helper_release(i); + + return ERROR_OK; +} + +static int helper_get_line(enum adapter_gpio_config_index idx) +{ + if (!is_gpio_config_valid(idx)) + return ERROR_OK; + + int dir = GPIOD_LINE_REQUEST_DIRECTION_INPUT, flags = 0, val = 0, retval; + + gpiod_chip[idx] = gpiod_chip_open_by_number(adapter_gpio_config[idx].chip_num); + if (!gpiod_chip[idx]) { + LOG_ERROR("Cannot open LinuxGPIOD chip %d for %s", adapter_gpio_config[idx].chip_num, + adapter_gpio_get_name(idx)); + return ERROR_JTAG_INIT_FAILED; + } + + gpiod_line[idx] = gpiod_chip_get_line(gpiod_chip[idx], adapter_gpio_config[idx].gpio_num); + if (!gpiod_line[idx]) { + LOG_ERROR("Error get line %s", adapter_gpio_get_name(idx)); + return ERROR_JTAG_INIT_FAILED; + } + + switch (adapter_gpio_config[idx].init_state) { + case ADAPTER_GPIO_INIT_STATE_INPUT: + dir = GPIOD_LINE_REQUEST_DIRECTION_INPUT; + break; + case ADAPTER_GPIO_INIT_STATE_INACTIVE: + dir = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + val = 0; + break; + case ADAPTER_GPIO_INIT_STATE_ACTIVE: + dir = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + val = 1; + break; + } + + switch (adapter_gpio_config[idx].drive) { + case ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL: + break; + case ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN: + flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; + break; + case ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE: + flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; + break; + } + + switch (adapter_gpio_config[idx].pull) { + case ADAPTER_GPIO_PULL_NONE: +#ifdef GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE + flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE; +#endif + break; + case ADAPTER_GPIO_PULL_UP: +#ifdef GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP + flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; +#else + LOG_WARNING("linuxgpiod: ignoring request for pull-up on %s: not supported by gpiod v%s", + adapter_gpio_get_name(idx), gpiod_version_string()); +#endif + break; + case ADAPTER_GPIO_PULL_DOWN: +#ifdef GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN + flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; +#else + LOG_WARNING("linuxgpiod: ignoring request for pull-down on %s: not supported by gpiod v%s", + adapter_gpio_get_name(idx), gpiod_version_string()); +#endif + break; + } + + if (adapter_gpio_config[idx].active_low) + flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + + struct gpiod_line_request_config config = { + .consumer = "OpenOCD", + .request_type = dir, + .flags = flags, + }; + + retval = gpiod_line_request(gpiod_line[idx], &config, val); + if (retval < 0) { + LOG_ERROR("Error requesting gpio line %s", adapter_gpio_get_name(idx)); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +static int linuxgpiod_init(void) +{ + LOG_INFO("Linux GPIOD JTAG/SWD bitbang driver"); + + bitbang_interface = &linuxgpiod_bitbang; + adapter_gpio_config = adapter_gpio_get_config(); + + /* + * Configure JTAG/SWD signals. Default directions and initial states are handled + * by adapter.c and "adapter gpio" command. + */ + + if (transport_is_jtag()) { + if (!linuxgpiod_jtag_mode_possible()) { + LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode"); + goto out_error; + } + + if (helper_get_line(ADAPTER_GPIO_IDX_TDO) != ERROR_OK || + helper_get_line(ADAPTER_GPIO_IDX_TDI) != ERROR_OK || + helper_get_line(ADAPTER_GPIO_IDX_TCK) != ERROR_OK || + helper_get_line(ADAPTER_GPIO_IDX_TMS) != ERROR_OK || + helper_get_line(ADAPTER_GPIO_IDX_TRST) != ERROR_OK) + goto out_error; + } + + if (transport_is_swd()) { + int retval1, retval2; + if (!linuxgpiod_swd_mode_possible()) { + LOG_ERROR("Require swclk and swdio gpio for SWD mode"); + goto out_error; + } + + /* + * swdio and its buffer should be initialized in the order that prevents + * two outputs from being connected together. This will occur if the + * swdio GPIO is configured as an output while the external buffer is + * configured to send the swdio signal from the target to the GPIO. + */ + if (adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO].init_state == ADAPTER_GPIO_INIT_STATE_INPUT) { + retval1 = helper_get_line(ADAPTER_GPIO_IDX_SWDIO); + retval2 = helper_get_line(ADAPTER_GPIO_IDX_SWDIO_DIR); + } else { + retval1 = helper_get_line(ADAPTER_GPIO_IDX_SWDIO_DIR); + retval2 = helper_get_line(ADAPTER_GPIO_IDX_SWDIO); + } + if (retval1 != ERROR_OK || retval2 != ERROR_OK) + goto out_error; + + if (helper_get_line(ADAPTER_GPIO_IDX_SWCLK) != ERROR_OK) + goto out_error; + } + + if (helper_get_line(ADAPTER_GPIO_IDX_SRST) != ERROR_OK || + helper_get_line(ADAPTER_GPIO_IDX_LED) != ERROR_OK) + goto out_error; + + return ERROR_OK; + +out_error: + linuxgpiod_quit(); + + return ERROR_JTAG_INIT_FAILED; +} + +static const char *const linuxgpiod_transport[] = { "swd", "jtag", NULL }; + +static struct jtag_interface linuxgpiod_interface = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = bitbang_execute_queue, +}; + +struct adapter_driver linuxgpiod_adapter_driver = { + .name = "linuxgpiod", + .transports = linuxgpiod_transport, + + .init = linuxgpiod_init, + .quit = linuxgpiod_quit, + .reset = linuxgpiod_reset, + + .jtag_ops = &linuxgpiod_interface, + .swd_ops = &bitbang_swd, +}; --