Add BCM2835 (as found in Raspberry Pi) interface driver

This adds support for JTAG programming by bitbanging GPIOs exposed on
the RaspberryPi's expansion header.

Tested by connecting directly to an STM32VLDiscovery board, without any
additional circuity. I observed maximum about 4MHz on the TCK pin with an
old analogue 'scope and about 100kHz when setting the speed to 100kHz.
Busyloop waiting is needed because even with a single 0ns nanosleep call
(with FIFO priority) it lowers the TCK speed to ~30kHz which is way too low
to be useful.

The speed testing with adapter_khz 2000 gave the following results:
sudo chrt -f 1 nice -n -19 ./src/openocd \
     -f interface/raspberrypi-native.cfg \
     -c "set WORKAREASIZE 0x2000" \
     -f target/stm32f1x.cfg -c "adapter_khz 2000"

wrote 131072 bytes from file random.bin in 3.973677s (32.212 KiB/s)
dumped 131072 bytes in 1.445699s (88.538 KiB/s)

This is 3.7 times faster for writing and 14.3 times faster for reading
compared to the generic sysfsgpio driver; probably the writing speed is
limited by the target itself here and reading speed might be considerably
higher too with appropriate connection and a capable target.

BCM2835 name is choosen over BCM2708 because the published peripherals
datasheet uses the particular model name and not family name.

Change-Id: Ib78168be27f53c2a3c88c3dd8154d1190c318c78
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-on: http://openocd.zylin.com/758
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
This commit is contained in:
Paul Fertser 2012-07-22 17:28:02 +04:00 committed by Spencer Oliver
parent 08d4411b59
commit 962b3eb40c
6 changed files with 442 additions and 0 deletions

View File

@ -422,11 +422,16 @@ case "${host_cpu}" in
AC_ARG_ENABLE([at91rm9200],
AS_HELP_STRING([--enable-at91rm9200], [Enable building support for AT91RM9200 based SBCs]),
[build_at91rm9200=$enableval], [build_at91rm9200=no])
AC_ARG_ENABLE([bcm2835gpio],
AS_HELP_STRING([--enable-bcm2835gpio], [Enable building support for bitbanging on BCM2835 (as found in Raspberry Pi)]),
[build_bcm2835gpio=$enableval], [build_bcm2835gpio=no])
;;
*)
build_ep93xx=no
build_at91rm9200=no
build_bcm2835gpio=no
;;
esac
@ -676,6 +681,13 @@ else
AC_DEFINE([BUILD_AT91RM9200], [0], [0 if you don't want at91rm9200.])
fi
if test $build_bcm2835gpio = yes; then
build_bitbang=yes
AC_DEFINE([BUILD_BCM2835GPIO], [1], [1 if you want bcm2835gpio.])
else
AC_DEFINE([BUILD_BCM2835GPIO], [0], [0 if you don't want bcm2835gpio.])
fi
if test x$parport_use_ppdev = xyes; then
AC_DEFINE([PARPORT_USE_PPDEV], [1], [1 if you want parport to use ppdev.])
else
@ -1208,6 +1220,7 @@ AM_CONDITIONAL([ZY1000], [test $build_zy1000 = yes])
AM_CONDITIONAL([ZY1000_MASTER], [test $build_zy1000_master = yes])
AM_CONDITIONAL([IOUTIL], [test $build_ioutil = yes])
AM_CONDITIONAL([AT91RM9200], [test $build_at91rm9200 = yes])
AM_CONDITIONAL([BCM2835GPIO], [test $build_bcm2835gpio = yes])
AM_CONDITIONAL([BITBANG], [test $build_bitbang = yes])
AM_CONDITIONAL([FT2232_LIBFTDI], [test $build_ft2232_libftdi = yes])
AM_CONDITIONAL([FT2232_DRIVER], [test $build_ft2232_ftd2xx = yes -o $build_ft2232_libftdi = yes])

View File

@ -574,6 +574,9 @@ produced, PDF schematics are easily found and it is easy to make.
@item @b{at91rm9200}
@* Like the EP93xx - but an ATMEL AT91RM9200 based solution using the GPIO pins on the chip.
@item @b{bcm2835gpio}
@* A BCM2835-based board (e.g. Raspberry Pi) using the GPIO pins of the expansion header.
@end itemize
@node About Jim-Tcl
@ -3048,6 +3051,22 @@ Turn power switch to target on/off.
No arguments: print status.
@end deffn
@deffn {Interface Driver} {bcm2835gpio}
This SoC is present in Raspberry Pi which is a cheap single-board computer
exposing some GPIOs on its expansion header.
The driver accesses memory-mapped GPIO peripheral registers directly
for maximum performance, but the only possible race condition is for
the pins' modes/muxing (which is highly unlikely), so it should be
able to coexist nicely with both sysfs bitbanging and various
peripherals' kernel drivers. The driver restores the previous
configuration on exit.
See @file{interface/raspberrypi-native.cfg} for a sample config and
pinout.
@end deffn
@section Transport Configuration
@cindex Transport
As noted earlier, depending on the version of OpenOCD you use,

View File

@ -114,6 +114,9 @@ endif
if SYSFSGPIO
DRIVERFILES += sysfsgpio.c
endif
if BCM2835GPIO
DRIVERFILES += bcm2835gpio.c
endif
if OPENJTAG
DRIVERFILES += openjtag.c

View File

@ -0,0 +1,368 @@
/***************************************************************************
* Copyright (C) 2013 by Paul Fertser, fercerpav@gmail.com *
* *
* Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
* Based on at91rm9200.c (c) Anders Larsen *
* and RPi GPIO examples by Gert van Loo & Dom *
* *
* 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. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <jtag/interface.h>
#include "bitbang.h"
#include <sys/mman.h>
#define BCM2835_PERI_BASE 0x20000000
#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) /* GPIO controller */
/* GPIO setup macros */
#define MODE_GPIO(g) (*(pio_base+((g)/10))>>(((g)%10)*3) & 7)
#define INP_GPIO(g) do { *(pio_base+((g)/10)) &= ~(7<<(((g)%10)*3)); } while (0)
#define SET_MODE_GPIO(g, m) do { /* clear the mode bits first, then set as necessary */ \
INP_GPIO(g); \
*(pio_base+((g)/10)) |= ((m)<<(((g)%10)*3)); } while (0)
#define OUT_GPIO(g) SET_MODE_GPIO(g, 1)
#define GPIO_SET (*(pio_base+7)) /* sets bits which are 1, ignores bits which are 0 */
#define GPIO_CLR (*(pio_base+10)) /* clears bits which are 1, ignores bits which are 0 */
#define GPIO_LEV (*(pio_base+13)) /* current level of the pin */
static int dev_mem_fd;
static volatile uint32_t *pio_base;
static int bcm2835gpio_read(void);
static void bcm2835gpio_write(int tck, int tms, int tdi);
static void bcm2835gpio_reset(int trst, int srst);
static int bcm2835gpio_init(void);
static int bcm2835gpio_quit(void);
static struct bitbang_interface bcm2835gpio_bitbang = {
.read = bcm2835gpio_read,
.write = bcm2835gpio_write,
.reset = bcm2835gpio_reset,
.blink = NULL
};
/* GPIO numbers for each signal. Negative values are invalid */
static int tck_gpio = -1;
static int tck_gpio_mode;
static int tms_gpio = -1;
static int tms_gpio_mode;
static int tdi_gpio = -1;
static int tdi_gpio_mode;
static int tdo_gpio = -1;
static int tdo_gpio_mode;
static int trst_gpio = -1;
static int trst_gpio_mode;
static int srst_gpio = -1;
static int srst_gpio_mode;
/* Transition delay coefficients */
static int speed_coeff = 113714;
static int speed_offset = 28;
static unsigned int jtag_delay;
static int bcm2835gpio_read(void)
{
return !!(GPIO_LEV & 1<<tdo_gpio);
}
static void bcm2835gpio_write(int tck, int tms, int tdi)
{
uint32_t set = tck<<tck_gpio | tms<<tms_gpio | tdi<<tdi_gpio;
uint32_t clear = !tck<<tck_gpio | !tms<<tms_gpio | !tdi<<tdi_gpio;
GPIO_SET = set;
GPIO_CLR = clear;
for (unsigned int i = 0; i < jtag_delay; i++)
asm volatile ("");
}
/* (1) assert or (0) deassert reset lines */
static void bcm2835gpio_reset(int trst, int srst)
{
uint32_t set = 0;
uint32_t clear = 0;
if (trst_gpio > 0) {
set |= !trst<<trst_gpio;
clear |= trst<<trst_gpio;
}
if (srst_gpio > 0) {
set |= !srst<<srst_gpio;
clear |= srst<<srst_gpio;
}
GPIO_SET = set;
GPIO_CLR = clear;
}
static int bcm2835gpio_khz(int khz, int *jtag_speed)
{
if (!khz) {
LOG_DEBUG("RCLK not supported");
return ERROR_FAIL;
}
*jtag_speed = speed_coeff/khz - speed_offset;
if (*jtag_speed < 0)
*jtag_speed = 0;
return ERROR_OK;
}
static int bcm2835gpio_speed_div(int speed, int *khz)
{
*khz = speed_coeff/(speed + speed_offset);
return ERROR_OK;
}
static int bcm2835gpio_speed(int speed)
{
jtag_delay = speed;
return ERROR_OK;
}
static int is_gpio_valid(int gpio)
{
return gpio >= 0 && gpio <= 53;
}
COMMAND_HANDLER(bcm2835gpio_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,
"BCM2835 GPIO config: tck = %d, tms = %d, tdi = %d, tdi = %d",
tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tck)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: tck = %d", tck_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tms)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: tms = %d", tms_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tdo)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: tdo = %d", tdo_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tdi)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: tdi = %d", tdi_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_srst)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: srst = %d", srst_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_trst)
{
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
command_print(CMD_CTX, "BCM2835 GPIO config: trst = %d", trst_gpio);
return ERROR_OK;
}
COMMAND_HANDLER(bcm2835gpio_handle_speed_coeffs)
{
if (CMD_ARGC == 2) {
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset);
}
return ERROR_OK;
}
static const struct command_registration bcm2835gpio_command_handlers[] = {
{
.name = "bcm2835gpio_jtag_nums",
.handler = &bcm2835gpio_handle_jtag_gpionums,
.mode = COMMAND_CONFIG,
.help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
.usage = "(tck tms tdi tdo)* ",
},
{
.name = "bcm2835gpio_tck_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_tck,
.mode = COMMAND_CONFIG,
.help = "gpio number for tck.",
},
{
.name = "bcm2835gpio_tms_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_tms,
.mode = COMMAND_CONFIG,
.help = "gpio number for tms.",
},
{
.name = "bcm2835gpio_tdo_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_tdo,
.mode = COMMAND_CONFIG,
.help = "gpio number for tdo.",
},
{
.name = "bcm2835gpio_tdi_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_tdi,
.mode = COMMAND_CONFIG,
.help = "gpio number for tdi.",
},
{
.name = "bcm2835gpio_srst_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_srst,
.mode = COMMAND_CONFIG,
.help = "gpio number for srst.",
},
{
.name = "bcm2835gpio_trst_num",
.handler = &bcm2835gpio_handle_jtag_gpionum_trst,
.mode = COMMAND_CONFIG,
.help = "gpio number for trst.",
},
{
.name = "bcm2835gpio_speed_coeffs",
.handler = &bcm2835gpio_handle_speed_coeffs,
.mode = COMMAND_CONFIG,
.help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.",
},
COMMAND_REGISTRATION_DONE
};
struct jtag_interface bcm2835gpio_interface = {
.name = "bcm2835gpio",
.supported = DEBUG_CAP_TMS_SEQ,
.execute_queue = bitbang_execute_queue,
.transports = jtag_only,
.speed = bcm2835gpio_speed,
.khz = bcm2835gpio_khz,
.speed_div = bcm2835gpio_speed_div,
.commands = bcm2835gpio_command_handlers,
.init = bcm2835gpio_init,
.quit = bcm2835gpio_quit,
};
static int bcm2835gpio_init(void)
{
bitbang_interface = &bcm2835gpio_bitbang;
if (!is_gpio_valid(tdo_gpio) || !is_gpio_valid(tdi_gpio) ||
!is_gpio_valid(tck_gpio) || !is_gpio_valid(tms_gpio) ||
(trst_gpio != -1 && !is_gpio_valid(trst_gpio)) ||
(srst_gpio != -1 && !is_gpio_valid(srst_gpio)))
return ERROR_JTAG_INIT_FAILED;
dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (dev_mem_fd < 0) {
perror("open");
return ERROR_JTAG_INIT_FAILED;
}
pio_base = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
MAP_SHARED, dev_mem_fd, BCM2835_GPIO_BASE);
if (pio_base == MAP_FAILED) {
perror("mmap");
close(dev_mem_fd);
return ERROR_JTAG_INIT_FAILED;
}
tdo_gpio_mode = MODE_GPIO(tdo_gpio);
tdi_gpio_mode = MODE_GPIO(tdi_gpio);
tck_gpio_mode = MODE_GPIO(tck_gpio);
tms_gpio_mode = MODE_GPIO(tms_gpio);
/*
* Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
* as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
*/
INP_GPIO(tdo_gpio);
GPIO_CLR = 1<<tdi_gpio | 1<<tck_gpio;
GPIO_SET = 1<<tms_gpio;
OUT_GPIO(tdi_gpio);
OUT_GPIO(tck_gpio);
OUT_GPIO(tms_gpio);
if (trst_gpio != -1) {
trst_gpio_mode = MODE_GPIO(trst_gpio);
GPIO_SET = 1 << trst_gpio;
OUT_GPIO(trst_gpio);
}
if (srst_gpio != -1) {
srst_gpio_mode = MODE_GPIO(srst_gpio);
GPIO_SET = 1 << srst_gpio;
OUT_GPIO(srst_gpio);
}
LOG_DEBUG("saved pinmux settings: tck %d tms %d tdi %d "
"tdo %d trst %d srst %d", tck_gpio_mode, tms_gpio_mode,
tdi_gpio_mode, tdo_gpio_mode, trst_gpio_mode, srst_gpio_mode);
return ERROR_OK;
}
static int bcm2835gpio_quit(void)
{
SET_MODE_GPIO(tdo_gpio, tdo_gpio_mode);
SET_MODE_GPIO(tdi_gpio, tdi_gpio_mode);
SET_MODE_GPIO(tck_gpio, tck_gpio_mode);
SET_MODE_GPIO(tms_gpio, tms_gpio_mode);
if (trst_gpio != -1)
SET_MODE_GPIO(trst_gpio, trst_gpio_mode);
if (srst_gpio != -1)
SET_MODE_GPIO(srst_gpio, srst_gpio_mode);
return ERROR_OK;
}

View File

@ -122,6 +122,9 @@ extern struct jtag_interface sysfsgpio_interface;
#if BUILD_AICE == 1
extern struct jtag_interface aice_interface;
#endif
#if BUILD_BCM2835GPIO == 1
extern struct jtag_interface bcm2835gpio_interface;
#endif
#endif /* standard drivers */
/**
@ -212,6 +215,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_AICE == 1
&aice_interface,
#endif
#if BUILD_BCM2835GPIO == 1
&bcm2835gpio_interface,
#endif
#endif /* standard drivers */
NULL,
};

View File

@ -0,0 +1,33 @@
#
# Config for using Raspberry Pi'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 and the cable is short enough.
#
# Do not forget the GND connection, pin 6 of the expansion header.
#
interface bcm2835gpio
# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET
# These depend on system clock, calibrated for stock 700MHz
# bcm2835gpio_speed SPEED_COEFF SPEED_OFFSET
bcm2835gpio_speed_coeffs 113714 28
# Each of the JTAG lines need a gpio number set: tck tms tdi tdo
# Header pin numbers: 23 22 19 21
bcm2835gpio_jtag_nums 11 25 10 9
# If you define trst or srst, use appropriate reset_config
# Header pin numbers: TRST - 26, SRST - 18
# bcm2835gpio_trst_num 7
# reset_config trst_only
# bcm2835gpio_srst_num 24
# reset_config srst_only srst_push_pull
# or if you have both connected,
# reset_config trst_and_srst srst_push_pull