jtag/adapter: Add command 'adapter gpio'

Most adapters define their own commands to obtain the GPIO number and
other GPIO configuration information such as chip number, output drive
type, active high/low.

Define a general command 'adapter gpio' as replacement for the
driver-specific ones.

Change-Id: I1ca9ca94f0c7df5713172e9f62ffb0ad64e9ee97
Signed-off-by: Steve Marple <stevemarple@googlemail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/6967
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Steve Marple 2022-05-04 22:51:48 +01:00 committed by Antonio Borneo
parent 3adbec9aab
commit 82fd400542
4 changed files with 474 additions and 0 deletions

View File

@ -2412,7 +2412,57 @@ when external configuration (such as jumpering) changes what
the hardware can support.
@end deffn
@anchor{adapter gpio}
@deffn {Config Command} {adapter gpio [ @
@option{tdo} | @option{tdi} | @option{tms} | @option{tck} | @option{trst} | @
@option{swdio} | @option{swdio_dir} | @option{swclk} | @option{srst} | @
@option{led} @
[ @
gpio_number | @option{-chip} chip_number | @
@option{-active-high} | @option{-active-low} | @
@option{-push-pull} | @option{-open-drain} | @option{-open-source} | @
@option{-pull-none} | @option{-pull-up} | @option{-pull-down} | @
@option{-init-inactive} | @option{-init-active} | @option{-init-input} @
] ]}
Define the GPIO mapping that the adapter will use. The following signals can be
defined:
@itemize @minus
@item @option{tdo}, @option{tdi}, @option{tms}, @option{tck}, @option{trst}:
JTAG transport signals
@item @option{swdio}, @option{swclk}: SWD transport signals
@item @option{swdio_dir}: optional swdio buffer control signal
@item @option{srst}: system reset signal
@item @option{led}: optional activity led
@end itemize
Some adapters require that the GPIO chip number is set in addition to the GPIO
number. The configuration options enable signals to be defined as active-high or
active-low. The output drive mode can be set to push-pull, open-drain or
open-source. Most adapters will have to emulate open-drain or open-source drive
modes by switching between an input and output. Input and output signals can be
instructed to use a pull-up or pull-down resistor, assuming it is supported by
the adaptor driver and hardware. The initial state of outputs may also be set,
"active" state means 1 for active-high outputs and 0 for active-low outputs.
Bidirectional signals may also be initialized as an input. If the swdio signal
is buffered the buffer direction can be controlled with the swdio_dir signal;
the active state means that the buffer should be set as an output with respect
to the adapter. The command options are cumulative with later commands able to
override settings defined by earlier ones. The two commands @command{gpio led 7
-active-high} and @command{gpio led -chip 1 -active-low} sent sequentially are
equivalent to issuing the single command @command{gpio led 7 -chip 1
-active-low}. It is not permissible to set the drive mode or initial state for
signals which are inputs. The drive mode for the srst and trst signals must be
set with the @command{adapter reset_config} command. It is not permissible to
set the initial state of swdio_dir as it is derived from the initial state of
swdio. The command @command{adapter gpio} prints the current configuration for
all GPIOs while the command @command{adapter gpio gpio_name} prints the current
configuration for gpio_name. Not all adapters support this generic GPIO mapping,
some require their own commands to define the GPIOs used. Adapters that support
the generic mapping may not support all of the listed options.
@end deffn
@deffn {Command} {adapter name}
Returns the name of the debug adapter driver being used.

View File

@ -48,13 +48,74 @@ static struct {
enum adapter_clk_mode clock_mode;
int speed_khz;
int rclk_fallback_speed_khz;
struct adapter_gpio_config gpios[ADAPTER_GPIO_IDX_NUM];
bool gpios_initialized; /* Initialization of GPIOs to their unset values performed at run time */
} adapter_config;
static const struct gpio_map {
const char *name;
enum adapter_gpio_direction direction;
bool permit_drive_option;
bool permit_init_state_option;
} gpio_map[ADAPTER_GPIO_IDX_NUM] = {
[ADAPTER_GPIO_IDX_TDO] = { "tdo", ADAPTER_GPIO_DIRECTION_INPUT, false, true, },
[ADAPTER_GPIO_IDX_TDI] = { "tdi", ADAPTER_GPIO_DIRECTION_OUTPUT, true, true, },
[ADAPTER_GPIO_IDX_TMS] = { "tms", ADAPTER_GPIO_DIRECTION_OUTPUT, true, true, },
[ADAPTER_GPIO_IDX_TCK] = { "tck", ADAPTER_GPIO_DIRECTION_OUTPUT, true, true, },
[ADAPTER_GPIO_IDX_SWDIO] = { "swdio", ADAPTER_GPIO_DIRECTION_BIDIRECTIONAL, true, true, },
[ADAPTER_GPIO_IDX_SWDIO_DIR] = { "swdio_dir", ADAPTER_GPIO_DIRECTION_OUTPUT, true, false, },
[ADAPTER_GPIO_IDX_SWCLK] = { "swclk", ADAPTER_GPIO_DIRECTION_OUTPUT, true, true, },
[ADAPTER_GPIO_IDX_TRST] = { "trst", ADAPTER_GPIO_DIRECTION_OUTPUT, false, true, },
[ADAPTER_GPIO_IDX_SRST] = { "srst", ADAPTER_GPIO_DIRECTION_OUTPUT, false, true, },
[ADAPTER_GPIO_IDX_LED] = { "led", ADAPTER_GPIO_DIRECTION_OUTPUT, true, true, },
};
bool is_adapter_initialized(void)
{
return adapter_config.adapter_initialized;
}
/* For convenience of the bit-banging drivers keep the gpio_config drive
* settings for srst and trst in sync with values set by the "adapter
* reset_config" command.
*/
static void sync_adapter_reset_with_gpios(void)
{
enum reset_types cfg = jtag_get_reset_config();
if (cfg & RESET_SRST_PUSH_PULL)
adapter_config.gpios[ADAPTER_GPIO_IDX_SRST].drive = ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL;
else
adapter_config.gpios[ADAPTER_GPIO_IDX_SRST].drive = ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN;
if (cfg & RESET_TRST_OPEN_DRAIN)
adapter_config.gpios[ADAPTER_GPIO_IDX_TRST].drive = ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN;
else
adapter_config.gpios[ADAPTER_GPIO_IDX_TRST].drive = ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL;
}
static void adapter_driver_gpios_init(void)
{
if (adapter_config.gpios_initialized)
return;
for (int i = 0; i < ADAPTER_GPIO_IDX_NUM; ++i) {
adapter_config.gpios[i].gpio_num = -1;
adapter_config.gpios[i].chip_num = -1;
if (gpio_map[i].direction == ADAPTER_GPIO_DIRECTION_INPUT)
adapter_config.gpios[i].init_state = ADAPTER_GPIO_INIT_STATE_INPUT;
}
/* Drivers assume active low, and this is the normal behaviour for reset
* lines so should be the default. */
adapter_config.gpios[ADAPTER_GPIO_IDX_SRST].active_low = true;
adapter_config.gpios[ADAPTER_GPIO_IDX_TRST].active_low = true;
sync_adapter_reset_with_gpios();
/* JTAG GPIOs should be inactive except for tms */
adapter_config.gpios[ADAPTER_GPIO_IDX_TMS].init_state = ADAPTER_GPIO_INIT_STATE_ACTIVE;
adapter_config.gpios_initialized = true;
}
/**
* Do low-level setup like initializing registers, output signals,
* and clocking.
@ -71,6 +132,8 @@ int adapter_init(struct command_context *cmd_ctx)
return ERROR_JTAG_INVALID_INTERFACE;
}
adapter_driver_gpios_init();
int retval;
if (adapter_config.clock_mode == CLOCK_MODE_UNSELECTED) {
@ -540,6 +603,8 @@ next:
old_cfg &= ~mask;
new_cfg |= old_cfg;
jtag_set_reset_config(new_cfg);
sync_adapter_reset_with_gpios();
} else
new_cfg = jtag_get_reset_config();
@ -770,6 +835,218 @@ COMMAND_HANDLER(handle_adapter_reset_de_assert)
(srst == VALUE_DEASSERT) ? SRST_DEASSERT : SRST_ASSERT);
}
static int get_gpio_index(const char *signal_name)
{
for (int i = 0; i < ADAPTER_GPIO_IDX_NUM; ++i) {
if (strcmp(gpio_map[i].name, signal_name) == 0)
return i;
}
return -1;
}
COMMAND_HELPER(helper_adapter_gpio_print_config, enum adapter_gpio_config_index gpio_idx)
{
struct adapter_gpio_config *gpio_config = &adapter_config.gpios[gpio_idx];
const char *active_state = gpio_config->active_low ? "low" : "high";
const char *dir = "";
const char *drive = "";
const char *pull = "";
const char *init_state = "";
switch (gpio_map[gpio_idx].direction) {
case ADAPTER_GPIO_DIRECTION_INPUT:
dir = "input";
break;
case ADAPTER_GPIO_DIRECTION_OUTPUT:
dir = "output";
break;
case ADAPTER_GPIO_DIRECTION_BIDIRECTIONAL:
dir = "bidirectional";
break;
}
if (gpio_map[gpio_idx].permit_drive_option) {
switch (gpio_config->drive) {
case ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL:
drive = ", push-pull";
break;
case ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN:
drive = ", open-drain";
break;
case ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE:
drive = ", open-source";
break;
}
}
switch (gpio_config->pull) {
case ADAPTER_GPIO_PULL_NONE:
pull = ", pull-none";
break;
case ADAPTER_GPIO_PULL_UP:
pull = ", pull-up";
break;
case ADAPTER_GPIO_PULL_DOWN:
pull = ", pull-down";
break;
}
if (gpio_map[gpio_idx].permit_init_state_option) {
switch (gpio_config->init_state) {
case ADAPTER_GPIO_INIT_STATE_INACTIVE:
init_state = ", init-state inactive";
break;
case ADAPTER_GPIO_INIT_STATE_ACTIVE:
init_state = ", init-state active";
break;
case ADAPTER_GPIO_INIT_STATE_INPUT:
init_state = ", init-state input";
break;
}
}
command_print(CMD, "adapter gpio %s (%s): num %d, chip %d, active-%s%s%s%s",
gpio_map[gpio_idx].name, dir, gpio_config->gpio_num, gpio_config->chip_num, active_state,
drive, pull, init_state);
return ERROR_OK;
}
COMMAND_HANDLER(helper_adapter_gpio_print_all_configs)
{
for (int i = 0; i < ADAPTER_GPIO_IDX_NUM; ++i)
CALL_COMMAND_HANDLER(helper_adapter_gpio_print_config, i);
return ERROR_OK;
}
COMMAND_HANDLER(adapter_gpio_config_handler)
{
unsigned int i = 1;
struct adapter_gpio_config *gpio_config;
adapter_driver_gpios_init();
if (CMD_ARGC == 0) {
CALL_COMMAND_HANDLER(helper_adapter_gpio_print_all_configs);
return ERROR_OK;
}
int gpio_idx = get_gpio_index(CMD_ARGV[0]);
if (gpio_idx == -1) {
LOG_ERROR("adapter has no gpio named %s", CMD_ARGV[0]);
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (CMD_ARGC == 1) {
CALL_COMMAND_HANDLER(helper_adapter_gpio_print_config, gpio_idx);
return ERROR_OK;
}
gpio_config = &adapter_config.gpios[gpio_idx];
while (i < CMD_ARGC) {
LOG_DEBUG("Processing %s", CMD_ARGV[i]);
if (isdigit(*CMD_ARGV[i])) {
int gpio_num; /* Use a meaningful output parameter for more helpful error messages */
COMMAND_PARSE_NUMBER(int, CMD_ARGV[i], gpio_num);
gpio_config->gpio_num = gpio_num;
++i;
continue;
}
if (strcmp(CMD_ARGV[i], "-chip") == 0) {
if (CMD_ARGC - i < 2) {
LOG_ERROR("-chip option requires a parameter");
return ERROR_FAIL;
}
LOG_DEBUG("-chip arg is %s", CMD_ARGV[i + 1]);
int chip_num; /* Use a meaningful output parameter for more helpful error messages */
COMMAND_PARSE_NUMBER(int, CMD_ARGV[i + 1], chip_num);
gpio_config->chip_num = chip_num;
i += 2;
continue;
}
if (strcmp(CMD_ARGV[i], "-active-high") == 0) {
++i;
gpio_config->active_low = false;
continue;
}
if (strcmp(CMD_ARGV[i], "-active-low") == 0) {
++i;
gpio_config->active_low = true;
continue;
}
if (gpio_map[gpio_idx].permit_drive_option) {
if (strcmp(CMD_ARGV[i], "-push-pull") == 0) {
++i;
gpio_config->drive = ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL;
continue;
}
if (strcmp(CMD_ARGV[i], "-open-drain") == 0) {
++i;
gpio_config->drive = ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN;
continue;
}
if (strcmp(CMD_ARGV[i], "-open-source") == 0) {
++i;
gpio_config->drive = ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE;
continue;
}
}
if (strcmp(CMD_ARGV[i], "-pull-none") == 0) {
++i;
gpio_config->pull = ADAPTER_GPIO_PULL_NONE;
continue;
}
if (strcmp(CMD_ARGV[i], "-pull-up") == 0) {
++i;
gpio_config->pull = ADAPTER_GPIO_PULL_UP;
continue;
}
if (strcmp(CMD_ARGV[i], "-pull-down") == 0) {
++i;
gpio_config->pull = ADAPTER_GPIO_PULL_DOWN;
continue;
}
if (gpio_map[gpio_idx].permit_init_state_option) {
if (strcmp(CMD_ARGV[i], "-init-inactive") == 0) {
++i;
gpio_config->init_state = ADAPTER_GPIO_INIT_STATE_INACTIVE;
continue;
}
if (strcmp(CMD_ARGV[i], "-init-active") == 0) {
++i;
gpio_config->init_state = ADAPTER_GPIO_INIT_STATE_ACTIVE;
continue;
}
if (gpio_map[gpio_idx].direction == ADAPTER_GPIO_DIRECTION_BIDIRECTIONAL &&
strcmp(CMD_ARGV[i], "-init-input") == 0) {
++i;
gpio_config->init_state = ADAPTER_GPIO_INIT_STATE_INPUT;
continue;
}
}
LOG_ERROR("illegal option for adapter %s %s: %s",
CMD_NAME, gpio_map[gpio_idx].name, CMD_ARGV[i]);
return ERROR_COMMAND_SYNTAX_ERROR;
}
/* Force swdio_dir init state to be compatible with swdio init state */
if (gpio_idx == ADAPTER_GPIO_IDX_SWDIO)
adapter_config.gpios[ADAPTER_GPIO_IDX_SWDIO_DIR].init_state =
(gpio_config->init_state == ADAPTER_GPIO_INIT_STATE_INPUT) ?
ADAPTER_GPIO_INIT_STATE_INACTIVE :
ADAPTER_GPIO_INIT_STATE_ACTIVE;
return ERROR_OK;
}
#ifdef HAVE_LIBUSB_GET_PORT_NUMBERS
COMMAND_HANDLER(handle_usb_location_command)
{
@ -887,6 +1164,19 @@ static const struct command_registration adapter_command_handlers[] = {
.help = "Controls SRST and TRST lines.",
.usage = "|assert [srst|trst [deassert|assert srst|trst]]",
},
{
.name = "gpio",
.handler = adapter_gpio_config_handler,
.mode = COMMAND_CONFIG,
.help = "gpio adapter command group",
.usage = "[ tdo|tdi|tms|tck|trst|swdio|swdio_dir|swclk|srst|led"
"[gpio_number] "
"[-chip chip_number] "
"[-active-high|-active-low] "
"[-push-pull|-open-drain|-open-source] "
"[-pull-none|-pull-up|-pull-down]"
"[-init-inactive|-init-active|-init-input] ]",
},
COMMAND_REGISTRATION_DONE
};
@ -923,3 +1213,14 @@ int adapter_register_commands(struct command_context *ctx)
{
return register_commands(ctx, NULL, interface_command_handlers);
}
const char *adapter_gpio_get_name(enum adapter_gpio_config_index idx)
{
return gpio_map[idx].name;
}
/* Allow drivers access to the GPIO configuration */
const struct adapter_gpio_config *adapter_gpio_get_config(void)
{
return adapter_config.gpios;
}

View File

@ -11,6 +11,59 @@
#include <stddef.h>
#include <stdint.h>
/** Supported output drive modes for adaptor GPIO */
enum adapter_gpio_drive_mode {
ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL,
ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN,
ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE,
};
/** Supported GPIO directions */
enum adapter_gpio_direction {
ADAPTER_GPIO_DIRECTION_INPUT,
ADAPTER_GPIO_DIRECTION_OUTPUT,
ADAPTER_GPIO_DIRECTION_BIDIRECTIONAL,
};
/** Supported initial states for GPIO */
enum adapter_gpio_init_state {
ADAPTER_GPIO_INIT_STATE_INACTIVE, /* Should be zero so it is the default state */
ADAPTER_GPIO_INIT_STATE_ACTIVE,
ADAPTER_GPIO_INIT_STATE_INPUT,
};
/** Supported pull directions for GPIO */
enum adapter_gpio_pull {
ADAPTER_GPIO_PULL_NONE,
ADAPTER_GPIO_PULL_UP,
ADAPTER_GPIO_PULL_DOWN,
};
/** Adapter GPIO */
enum adapter_gpio_config_index {
ADAPTER_GPIO_IDX_TDO,
ADAPTER_GPIO_IDX_TDI,
ADAPTER_GPIO_IDX_TMS,
ADAPTER_GPIO_IDX_TCK,
ADAPTER_GPIO_IDX_TRST,
ADAPTER_GPIO_IDX_SWDIO,
ADAPTER_GPIO_IDX_SWDIO_DIR,
ADAPTER_GPIO_IDX_SWCLK,
ADAPTER_GPIO_IDX_SRST,
ADAPTER_GPIO_IDX_LED,
ADAPTER_GPIO_IDX_NUM, /* must be the last item */
};
/** Configuration options for a single GPIO */
struct adapter_gpio_config {
int gpio_num;
int chip_num;
enum adapter_gpio_drive_mode drive; /* For outputs only */
enum adapter_gpio_init_state init_state;
bool active_low;
enum adapter_gpio_pull pull;
};
struct command_context;
/** Register the adapter's commands */
@ -58,4 +111,14 @@ unsigned int adapter_get_speed_khz(void);
/** Retrieves the serial number set with command 'adapter serial' */
const char *adapter_get_required_serial(void);
/**
* Retrieves gpio name
*/
const char *adapter_gpio_get_name(enum adapter_gpio_config_index idx);
/**
* Retrieves gpio configuration set with command 'adapter gpio <signal_name>'
*/
const struct adapter_gpio_config *adapter_gpio_get_config(void);
#endif /* OPENOCD_JTAG_ADAPTER_H */

View File

@ -122,6 +122,66 @@ proc jtag_ntrst_assert_width args {
#
# FIXME phase these aids out after some releases
#
lappend _telnet_autocomplete_skip adapter_gpio_helper_with_caller
# Helper for deprecated driver functions that should call "adapter gpio XXX".
# Call this function as:
# adapter_gpio_helper_with_caller caller sig_name
# adapter_gpio_helper_with_caller caller sig_name gpio_num
# adapter_gpio_helper_with_caller caller sig_name chip_num gpio_num
proc adapter_gpio_helper_with_caller {caller sig_name args} {
echo "DEPRECATED! use 'adapter gpio $sig_name' not '$caller'"
switch [llength $args] {
0 {}
1 {eval adapter gpio $sig_name $args}
2 {eval adapter gpio $sig_name [lindex $args 1] -chip [lindex $args 0]}
default {return -code 1 -level 1 "$caller: syntax error"}
}
eval adapter gpio $sig_name
}
lappend _telnet_autocomplete_skip adapter_gpio_helper
# Call this function as:
# adapter_gpio_helper sig_name
# adapter_gpio_helper sig_name gpio_num
# adapter_gpio_helper sig_name chip_num gpio_num
proc adapter_gpio_helper {sig_name args} {
set caller [lindex [info level -1] 0]
eval adapter_gpio_helper_with_caller {"$caller"} $sig_name $args
}
lappend _telnet_autocomplete_skip adapter_gpio_jtag_nums_with_caller
# Helper for deprecated driver functions that implemented jtag_nums
proc adapter_gpio_jtag_nums_with_caller {caller tck_num tms_num tdi_num tdo_num} {
echo "DEPRECATED! use 'adapter gpio tck; adapter gpio tms; adapter gpio tdi; adapter gpio tdo' not '$caller'"
eval adapter gpio tck $tck_num
eval adapter gpio tms $tms_num
eval adapter gpio tdi $tdi_num
eval adapter gpio tdo $tdo_num
}
lappend _telnet_autocomplete_skip adapter_gpio_jtag_nums
# Helper for deprecated driver functions that implemented jtag_nums
proc adapter_gpio_jtag_nums {args} {
set caller [lindex [info level -1] 0]
eval adapter_gpio_jtag_nums_with_caller {"$caller"} $args
}
lappend _telnet_autocomplete_skip adapter_gpio_swd_nums_with_caller
# Helper for deprecated driver functions that implemented swd_nums
proc adapter_gpio_swd_nums_with_caller {caller swclk_num swdio_num} {
echo "DEPRECATED! use 'adapter gpio swclk; adapter gpio swdio' not '$caller'"
eval adapter gpio swclk $swclk_num
eval adapter gpio swdio $swdio_num
}
lappend _telnet_autocomplete_skip adapter_gpio_swd_nums
# Helper for deprecated driver functions that implemented jtag_nums
proc adapter_gpio_swd_nums {args} {
set caller [lindex [info level -1] 0]
eval adapter_gpio_swd_nums_with_caller {"$caller"} $args
}
lappend _telnet_autocomplete_skip jtag_reset
proc jtag_reset args {
echo "DEPRECATED! use 'adapter \[de\]assert' not 'jtag_reset'"