aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Fertser <fercerpav@gmail.com>2012-07-22 17:28:02 +0400
committerSpencer Oliver <spen@spen-soft.co.uk>2013-06-10 09:39:48 +0000
commit962b3eb40cc5b69d9b3d6fcc4c5d56c4d204a307 (patch)
treebbc0bc9979304808c9ab5292365169bf61e7ac2c
parent08d4411b59dd8bd0e7d8009003b71d23acbf6eee (diff)
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>
-rw-r--r--configure.ac13
-rw-r--r--doc/openocd.texi19
-rw-r--r--src/jtag/drivers/Makefile.am3
-rw-r--r--src/jtag/drivers/bcm2835gpio.c368
-rw-r--r--src/jtag/interfaces.c6
-rw-r--r--tcl/interface/raspberrypi-native.cfg33
6 files changed, 442 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 68d6301a..4388573c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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])
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 9ea7edf8..51dab187 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -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,
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 65167ea2..30251be1 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -114,6 +114,9 @@ endif
if SYSFSGPIO
DRIVERFILES += sysfsgpio.c
endif
+if BCM2835GPIO
+DRIVERFILES += bcm2835gpio.c
+endif
if OPENJTAG
DRIVERFILES += openjtag.c
diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c
new file mode 100644
index 00000000..7dfabcb4
--- /dev/null
+++ b/src/jtag/drivers/bcm2835gpio.c
@@ -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;
+}
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index b967b7e9..579e9e74 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -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,
};
diff --git a/tcl/interface/raspberrypi-native.cfg b/tcl/interface/raspberrypi-native.cfg
new file mode 100644
index 00000000..058d80bf
--- /dev/null
+++ b/tcl/interface/raspberrypi-native.cfg
@@ -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
+