drivers: new jtag bitbang driver using sysfs gpio

This driver implements a bitbang jtag interface using gpio lines exported via
sysfs.

The aim of this driver implementation is to use system GPIOs but to avoid the
need for an additional kernel driver.

A config suitable for RaspberryPi is included.

Change-Id: Ib2acf720247a219768d1cbfeebd88057ed2d7b8b
Signed-off-by: Marc Reilly <marc@cpdesign.com.au>
Reviewed-on: http://openocd.zylin.com/762
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Tested-by: jenkins
Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>
This commit is contained in:
Marc Reilly 2012-08-20 09:46:07 +10:00 committed by Freddie Chopin
parent a4830e7a6a
commit fe52282c37
5 changed files with 549 additions and 0 deletions

View File

@ -487,6 +487,10 @@ AC_ARG_ENABLE([opendous],
AS_HELP_STRING([--enable-opendous], [Enable building support for the estick/opendous JTAG Programmer]),
[build_opendous=$enableval], [build_opendous=no])
AC_ARG_ENABLE([sysfsgpio],
AS_HELP_STRING([--enable-sysfsgpio], [Enable building support for programming driven via sysfs gpios.]),
[build_sysfsgpio=$enableval], [build_sysfsgpio=no])
AC_ARG_ENABLE([minidriver_dummy],
AS_HELP_STRING([--enable-minidriver-dummy], [Enable the dummy minidriver.]),
[build_minidriver_dummy=$enableval], [build_minidriver_dummy=no])
@ -819,6 +823,12 @@ else
AC_DEFINE([BUILD_REMOTE_BITBANG], [0], [0 if you don't want the Remote Bitbang JTAG driver.])
fi
if test $build_sysfsgpio = yes; then
build_bitbang=yes
AC_DEFINE([BUILD_SYSFSGPIO], [1], [1 if you want the SysfsGPIO driver.])
else
AC_DEFINE([BUILD_SYSFSGPIO], [0], [0 if you don't want SysfsGPIO driver.])
fi
#-- Deal with MingW/Cygwin FTD2XX issues
if test $is_win32 = yes; then
@ -1185,6 +1195,7 @@ AM_CONDITIONAL([BUSPIRATE], [test $build_buspirate = yes])
AM_CONDITIONAL([STLINK], [test $build_stlink = yes])
AM_CONDITIONAL([OSBDM], [test $build_osbdm = yes])
AM_CONDITIONAL([OPENDOUS], [test $build_opendous = yes])
AM_CONDITIONAL([SYSFSGPIO], [test $build_sysfsgpio = yes])
AM_CONDITIONAL([USB], [test $build_usb = yes])
AM_CONDITIONAL([USB_NG], [test $build_usb_ng = yes])
AM_CONDITIONAL([USE_LIBUSB0], [test $use_libusb0 = yes])

View File

@ -107,6 +107,9 @@ endif
if OPENDOUS
DRIVERFILES += opendous.c
endif
if SYSFSGPIO
DRIVERFILES += sysfsgpio.c
endif
noinst_HEADERS = \
bitbang.h \

View File

@ -0,0 +1,505 @@
/***************************************************************************
* Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/**
* @file
* This driver implements a bitbang jtag interface using gpio lines via
* sysfs.
* The aim of this driver implementation is use system GPIOs but avoid the
* need for a additional kernel driver.
* (Note memory mapped IO is another option, however it doesn't mix well with
* the kernel gpiolib driver - which makes sense I guess.)
*
* A gpio is required for tck, tms, tdi and tdo. One or both of srst and trst
* must be also be specified. The required jtag gpios are specified via the
* sysfsgpio_jtag_nums command or the relevant sysfsgpio_XXX_num commang.
* The srst and trst gpios are set via the sysfsgpio_srst_num and
* sysfsgpio_trst_num respectively. GPIO numbering follows the kernel
* convention of starting from 0.
*
* The gpios should not be in use by another entity, and must not be requested
* by a kernel driver without also being exported by it (otherwise they can't
* be exported by sysfs).
*
* The sysfs gpio interface can only manipulate one gpio at a time, so the
* bitbang write handler remembers the last state for tck, tms, tdi to avoid
* superfluous writes.
* For speed the sysfs "value" entry is opened at init and held open.
* This results in considerable gains over open-write-close (45s vs 900s)
*
* Further work could address:
* -srst and trst open drain/ push pull
* -configurable active high/low for srst & trst
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <jtag/interface.h>
#include "bitbang.h"
/*
* Helper func to determine if gpio number valid
*
* Assume here that there will be less than 1000 gpios on a system
*/
static int is_gpio_valid(int gpio)
{
return gpio >= 0 && gpio < 1000;
}
/*
* Helper func to open, write to and close a file
* name and valstr must be null terminated.
*
* Returns negative on failure.
*/
static int open_write_close(const char *name, const char *valstr)
{
int ret;
int fd = open(name, O_WRONLY);
if (fd < 0)
return fd;
ret = write(fd, valstr, strlen(valstr));
close(fd);
return ret;
}
/*
* Helper func to unexport gpio from sysfs
*/
static void unexport_sysfs_gpio(int gpio)
{
char gpiostr[4];
if (!is_gpio_valid(gpio))
return;
snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
if (open_write_close("/sys/class/gpio/unexport", gpiostr) < 0)
LOG_ERROR("Couldn't unexport gpio %d", gpio);
return;
}
/*
* Exports and sets up direction for gpio.
* If the gpio is an output, it is initialized according to init_high,
* otherwise it is ignored.
*
* If the gpio is already exported we just show a warning and continue; if
* openocd happened to crash (or was killed by user) then the gpios will not
* have been cleaned up.
*/
static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
{
char buf[40];
char gpiostr[4];
int ret;
if (!is_gpio_valid(gpio))
return ERROR_OK;
snprintf(gpiostr, sizeof(gpiostr), "%d", gpio);
ret = open_write_close("/sys/class/gpio/export", gpiostr);
if (ret < 0) {
if (errno == EBUSY) {
LOG_WARNING("gpio %d is already exported", gpio);
} else {
LOG_ERROR("Couldn't export gpio %d", gpio);
perror("sysfsgpio: ");
return ERROR_FAIL;
}
}
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
if (ret < 0) {
LOG_ERROR("Couldn't set direction for gpio %d", gpio);
perror("sysfsgpio: ");
unexport_sysfs_gpio(gpio);
return ERROR_FAIL;
}
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
if (is_output)
ret = open(buf, O_WRONLY | O_NONBLOCK | O_SYNC);
else
ret = open(buf, O_RDONLY | O_NONBLOCK | O_SYNC);
if (ret < 0)
unexport_sysfs_gpio(gpio);
return ret;
}
/*
* file descriptors for /sys/class/gpio/gpioXX/value
* Set up during init.
*/
static int tck_fd = -1;
static int tms_fd = -1;
static int tdi_fd = -1;
static int tdo_fd = -1;
static int trst_fd = -1;
static int srst_fd = -1;
/*
* Bitbang interface read of TDO
*
* The sysfs value will read back either '0' or '1'. The trick here is to call
* lseek to bypass buffering in the sysfs kernel driver.
*/
static int sysfsgpio_read(void)
{
char buf[1];
/* important to seek to signal sysfs of new read */
lseek(tdo_fd, 0, SEEK_SET);
int ret = read(tdo_fd, &buf, sizeof(buf));
if (ret < 0) {
LOG_WARNING("reading tdo failed");
return 0;
}
return buf[0] == '1';
}
/*
* 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 void sysfsgpio_write(int tck, int tms, int tdi)
{
const char one[] = "1";
const char zero[] = "0";
static int last_tck;
static int last_tms;
static int last_tdi;
static int first_time;
if (!first_time) {
last_tck = !tck;
last_tms = !tms;
last_tdi = !tdi;
first_time = 1;
}
if (tdi != last_tdi)
write(tdi_fd, tdi ? &one : &zero, 1);
if (tms != last_tms)
write(tms_fd, tms ? &one : &zero, 1);
/* write clk last */
if (tck != last_tck)
write(tck_fd, tck ? &one : &zero, 1);
last_tdi = tdi;
last_tms = tms;
last_tck = tck;
}
/*
* Bitbang interface to manipulate reset lines SRST and TRST
*
* (1) assert or (0) deassert reset lines
*/
static void sysfsgpio_reset(int trst, int srst)
{
const char one[] = "1";
const char zero[] = "0";
/* assume active low */
if (srst_fd >= 0)
write(srst_fd, srst ? &zero : &one, 1);
/* assume active low */
if (trst_fd >= 0)
write(trst_fd, trst ? &zero : &one, 1);
}
/* No speed control is implemented yet */
static int sysfsgpio_speed(int speed)
{
return ERROR_OK;
}
static int sysfsgpio_khz(int khz, int *jtag_speed)
{
/* no adaptive clocking */
if (khz == 0)
return ERROR_FAIL;
*jtag_speed = 0;
return ERROR_OK;
}
static int sysfsgpio_speed_div(int speed, int *khz)
{
*khz = 1;
return ERROR_OK;
}
/* gpio numbers for each gpio. Negative values are invalid */
static int tck_gpio = -1;
static int tms_gpio = -1;
static int tdi_gpio = -1;
static int tdo_gpio = -1;
static int trst_gpio = -1;
static int srst_gpio = -1;
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionums)
{
if (CMD_ARGC == 4) {
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
} else if (CMD_ARGC != 0) {
return ERROR_COMMAND_SYNTAX_ERROR;
}
command_print(CMD_CTX,
"SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdi = %d",
tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tck)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
command_print(CMD_CTX, "SysfsGPIO num: tck = %d", tck_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tms)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
command_print(CMD_CTX, "SysfsGPIO num: tms = %d", tms_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdo)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
command_print(CMD_CTX, "SysfsGPIO num: tdo = %d", tdo_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_tdi)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
command_print(CMD_CTX, "SysfsGPIO num: tdi = %d", tdi_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_srst)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
command_print(CMD_CTX, "SysfsGPIO num: srst = %d", srst_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(sysfsgpio_handle_jtag_gpionum_trst)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
command_print(CMD_CTX, "SysfsGPIO num: trst = %d", trst_gpio);
return ERROR_OK;
}
static const struct command_registration sysfsgpio_command_handlers[] = {
{
.name = "sysfsgpio_jtag_nums",
.handler = &sysfsgpio_handle_jtag_gpionums,
.mode = COMMAND_CONFIG,
.help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
.usage = "(tck tms tdi tdo)* ",
},
{
.name = "sysfsgpio_tck_num",
.handler = &sysfsgpio_handle_jtag_gpionum_tck,
.mode = COMMAND_CONFIG,
.help = "gpio number for tck.",
},
{
.name = "sysfsgpio_tms_num",
.handler = &sysfsgpio_handle_jtag_gpionum_tms,
.mode = COMMAND_CONFIG,
.help = "gpio number for tms.",
},
{
.name = "sysfsgpio_tdo_num",
.handler = &sysfsgpio_handle_jtag_gpionum_tdo,
.mode = COMMAND_CONFIG,
.help = "gpio number for tdo.",
},
{
.name = "sysfsgpio_tdi_num",
.handler = &sysfsgpio_handle_jtag_gpionum_tdi,
.mode = COMMAND_CONFIG,
.help = "gpio number for tdi.",
},
{
.name = "sysfsgpio_srst_num",
.handler = &sysfsgpio_handle_jtag_gpionum_srst,
.mode = COMMAND_CONFIG,
.help = "gpio number for srst.",
},
{
.name = "sysfsgpio_trst_num",
.handler = &sysfsgpio_handle_jtag_gpionum_trst,
.mode = COMMAND_CONFIG,
.help = "gpio number for trst.",
},
COMMAND_REGISTRATION_DONE
};
static int sysfsgpio_init(void);
static int sysfsgpio_quit(void);
struct jtag_interface sysfsgpio_interface = {
.name = "sysfsgpio",
.supported = DEBUG_CAP_TMS_SEQ,
.execute_queue = bitbang_execute_queue,
.transports = jtag_only,
.speed = sysfsgpio_speed,
.khz = sysfsgpio_khz,
.speed_div = sysfsgpio_speed_div,
.commands = sysfsgpio_command_handlers,
.init = sysfsgpio_init,
.quit = sysfsgpio_quit,
};
static struct bitbang_interface sysfsgpio_bitbang = {
.read = sysfsgpio_read,
.write = sysfsgpio_write,
.reset = sysfsgpio_reset,
.blink = 0
};
/* helper func to close and cleanup files only if they were valid/ used */
static void cleanup_fd(int fd, int gpio)
{
if (gpio >= 0) {
if (fd >= 0)
close(fd);
unexport_sysfs_gpio(gpio);
}
}
static void cleanup_all_fds(void)
{
cleanup_fd(tck_fd, tck_gpio);
cleanup_fd(tms_fd, tms_gpio);
cleanup_fd(tdi_fd, tdi_gpio);
cleanup_fd(tdo_fd, tdo_gpio);
cleanup_fd(trst_fd, trst_gpio);
cleanup_fd(srst_fd, srst_gpio);
}
static int sysfsgpio_init(void)
{
bitbang_interface = &sysfsgpio_bitbang;
LOG_INFO("SysfsGPIO JTAG bitbang driver");
if (!(is_gpio_valid(tck_gpio)
&& is_gpio_valid(tms_gpio)
&& is_gpio_valid(tdi_gpio)
&& is_gpio_valid(tdo_gpio))) {
if (!is_gpio_valid(tck_gpio))
LOG_ERROR("gpio num for tck is invalid");
if (!is_gpio_valid(tms_gpio))
LOG_ERROR("gpio num for tms is invalid");
if (!is_gpio_valid(tdo_gpio))
LOG_ERROR("gpio num for tdo is invalid");
if (!is_gpio_valid(tdi_gpio))
LOG_ERROR("gpio num for tdi is invalid");
LOG_ERROR("Require tck, tms, tdi and tdo gpios to all be specified");
return ERROR_JTAG_INIT_FAILED;
}
if (!is_gpio_valid(trst_gpio) && !is_gpio_valid(srst_gpio)) {
LOG_ERROR("Require at least one of trst or srst gpios to be specified");
return ERROR_JTAG_INIT_FAILED;
}
/*
* Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
* as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
*/
tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
if (tck_fd < 0)
goto out_error;
tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
if (tms_fd < 0)
goto out_error;
tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
if (tdi_fd < 0)
goto out_error;
tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
if (tdo_fd < 0)
goto out_error;
/* assume active low*/
trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
if (trst_gpio > 0 && trst_fd < 0)
goto out_error;
/* assume active low*/
srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
if (srst_gpio > 0 && srst_fd < 0)
goto out_error;
return ERROR_OK;
out_error:
cleanup_all_fds();
return ERROR_JTAG_INIT_FAILED;
}
static int sysfsgpio_quit(void)
{
cleanup_all_fds();
return ERROR_OK;
}

View File

@ -113,6 +113,9 @@ extern struct jtag_interface osbdm_interface;
#if BUILD_OPENDOUS == 1
extern struct jtag_interface opendous_interface;
#endif
#if BUILD_SYSFSGPIO == 1
extern struct jtag_interface sysfsgpio_interface;
#endif
#endif /* standard drivers */
/**
@ -194,6 +197,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_OPENDOUS == 1
&opendous_interface,
#endif
#if BUILD_SYSFSGPIO == 1
&sysfsgpio_interface,
#endif
#endif /* standard drivers */
NULL,
};

View File

@ -0,0 +1,24 @@
#
# Config for using RaspberryPi's expansion header
#
# This is best used with a fast enough buffer but also
# is suitable for direct connection if the target voltage
# matches RPi's 3.3V
#
# Do not forget the GND connection, pin 6 of the expansion header.
#
interface sysfsgpio
# This has no effect on the driver, but is required
adapter_khz 100
# Each of the JTAG lines need a gpio number set: tck tms tdi tdo
# Header pin numbers: 23 22 19 21
sysfsgpio_jtag_nums 11 25 10 9
# At least one of srst or trst needs to be specified
# Header pin numbers: TRST - 26, SRST - 18
sysfsgpio_trst_num 7
# sysfsgpio_srst_num 24