aboutsummaryrefslogtreecommitdiff
path: root/src
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 /src
parent08d4411b59dd8bd0e7d8009003b71d23acbf6eee (diff)
downloadriscv-openocd-962b3eb40cc5b69d9b3d6fcc4c5d56c4d204a307.zip
riscv-openocd-962b3eb40cc5b69d9b3d6fcc4c5d56c4d204a307.tar.gz
riscv-openocd-962b3eb40cc5b69d9b3d6fcc4c5d56c4d204a307.tar.bz2
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>
Diffstat (limited to 'src')
-rw-r--r--src/jtag/drivers/Makefile.am3
-rw-r--r--src/jtag/drivers/bcm2835gpio.c368
-rw-r--r--src/jtag/interfaces.c6
3 files changed, 377 insertions, 0 deletions
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 65167ea..30251be 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 0000000..7dfabcb
--- /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 b967b7e..579e9e7 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,
};