From 962b3eb40cc5b69d9b3d6fcc4c5d56c4d204a307 Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Sun, 22 Jul 2012 17:28:02 +0400 Subject: [PATCH] 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 Reviewed-on: http://openocd.zylin.com/758 Tested-by: jenkins Reviewed-by: Spencer Oliver --- configure.ac | 13 + doc/openocd.texi | 19 ++ src/jtag/drivers/Makefile.am | 3 + src/jtag/drivers/bcm2835gpio.c | 368 +++++++++++++++++++++++++++ src/jtag/interfaces.c | 6 + tcl/interface/raspberrypi-native.cfg | 33 +++ 6 files changed, 442 insertions(+) create mode 100644 src/jtag/drivers/bcm2835gpio.c create mode 100644 tcl/interface/raspberrypi-native.cfg diff --git a/configure.ac b/configure.ac index 68d6301a9..4388573c0 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 9ea7edf83..51dab187f 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 65167ea27..30251be15 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 000000000..7dfabcb45 --- /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 +#include "bitbang.h" + +#include + +#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< 0) { + set |= !trst< 0) { + set |= !srst<= 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<