aboutsummaryrefslogtreecommitdiff
path: root/src/jtag/drivers/usb_blaster
diff options
context:
space:
mode:
Diffstat (limited to 'src/jtag/drivers/usb_blaster')
-rw-r--r--src/jtag/drivers/usb_blaster/Makefile.am6
-rw-r--r--src/jtag/drivers/usb_blaster/ublast2_access_libusb.c253
-rw-r--r--src/jtag/drivers/usb_blaster/ublast_access.h16
-rw-r--r--src/jtag/drivers/usb_blaster/usb_blaster.c81
4 files changed, 345 insertions, 11 deletions
diff --git a/src/jtag/drivers/usb_blaster/Makefile.am b/src/jtag/drivers/usb_blaster/Makefile.am
index 1cf7ba99..95e84fec 100644
--- a/src/jtag/drivers/usb_blaster/Makefile.am
+++ b/src/jtag/drivers/usb_blaster/Makefile.am
@@ -1,5 +1,7 @@
include $(top_srcdir)/common.mk
+AM_CPPFLAGS += -I$(top_srcdir)/src/jtag/drivers $(LIBUSB1_CFLAGS)
+
noinst_LTLIBRARIES = libocdusbblaster.la
libocdusbblaster_la_SOURCES = $(USB_BLASTER_SRC)
@@ -13,6 +15,10 @@ if USB_BLASTER_FTD2XX
USB_BLASTER_SRC += ublast_access_ftd2xx.c
endif
+if USB_BLASTER_2
+USB_BLASTER_SRC += ublast2_access_libusb.c
+endif
+
noinst_HEADERS = ublast_access.h
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
diff --git a/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c
new file mode 100644
index 00000000..437150d7
--- /dev/null
+++ b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c
@@ -0,0 +1,253 @@
+/*
+ * Driver for USB-JTAG, Altera USB-Blaster II and compatibles
+ *
+ * Copyright (C) 2013 Franck Jullien franck.jullien@gmail.com
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <libusb_common.h>
+#include <target/image.h>
+
+#include "ublast_access.h"
+
+#define USBBLASTER_CTRL_READ_REV 0x94
+#define USBBLASTER_CTRL_LOAD_FIRM 0xA0
+#define USBBLASTER_EPOUT 4
+#define USBBLASTER_EPIN 8
+
+#define EZUSB_CPUCS 0xe600
+#define CPU_RESET 1
+
+/** Maximum size of a single firmware section. Entire EZ-USB code space = 16kB */
+#define SECTION_BUFFERSIZE 16384
+
+static int ublast2_libusb_read(struct ublast_lowlevel *low, uint8_t *buf,
+ unsigned size, uint32_t *bytes_read)
+{
+ *bytes_read = jtag_libusb_bulk_read(low->libusb_dev,
+ USBBLASTER_EPIN | \
+ LIBUSB_ENDPOINT_IN,
+ (char *)buf,
+ size,
+ 100);
+ return ERROR_OK;
+}
+
+static int ublast2_libusb_write(struct ublast_lowlevel *low, uint8_t *buf,
+ int size, uint32_t *bytes_written)
+{
+ *bytes_written = jtag_libusb_bulk_write(low->libusb_dev,
+ USBBLASTER_EPOUT | \
+ LIBUSB_ENDPOINT_OUT,
+ (char *)buf,
+ size,
+ 100);
+ return ERROR_OK;
+}
+
+static int ublast2_write_firmware_section(struct jtag_libusb_device_handle *libusb_dev,
+ struct image *firmware_image, int section_index)
+{
+ uint16_t chunk_size;
+ uint8_t data[SECTION_BUFFERSIZE];
+ uint8_t *data_ptr = data;
+ size_t size_read;
+
+ uint16_t size = (uint16_t)firmware_image->sections[section_index].size;
+ uint16_t addr = (uint16_t)firmware_image->sections[section_index].base_address;
+
+ LOG_DEBUG("section %02i at addr 0x%04x (size 0x%04x)", section_index, addr,
+ size);
+
+ if (data == NULL)
+ return ERROR_FAIL;
+
+ /* Copy section contents to local buffer */
+ int ret = image_read_section(firmware_image, section_index, 0, size, data,
+ &size_read);
+
+ if ((ret != ERROR_OK) || (size_read != size)) {
+ /* Propagating the return code would return '0' (misleadingly indicating
+ * successful execution of the function) if only the size check fails. */
+ return ERROR_FAIL;
+ }
+
+ uint16_t bytes_remaining = size;
+
+ /* Send section data in chunks of up to 64 bytes to ULINK */
+ while (bytes_remaining > 0) {
+ if (bytes_remaining > 64)
+ chunk_size = 64;
+ else
+ chunk_size = bytes_remaining;
+
+ jtag_libusb_control_transfer(libusb_dev,
+ LIBUSB_REQUEST_TYPE_VENDOR | \
+ LIBUSB_ENDPOINT_OUT,
+ USBBLASTER_CTRL_LOAD_FIRM,
+ addr,
+ 0,
+ (char *)data_ptr,
+ chunk_size,
+ 100);
+
+ bytes_remaining -= chunk_size;
+ addr += chunk_size;
+ data_ptr += chunk_size;
+ }
+
+ return ERROR_OK;
+}
+
+static int load_usb_blaster_firmware(struct jtag_libusb_device_handle *libusb_dev,
+ struct ublast_lowlevel *low)
+{
+ struct image ublast2_firmware_image;
+
+ if (!low->firmware_path) {
+ LOG_ERROR("No firmware path specified");
+ return ERROR_FAIL;
+ }
+
+ ublast2_firmware_image.base_address = 0;
+ ublast2_firmware_image.base_address_set = 0;
+
+ int ret = image_open(&ublast2_firmware_image, low->firmware_path, "ihex");
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Could not load firmware image");
+ return ret;
+ }
+
+ /** A host loader program must write 0x01 to the CPUCS register
+ * to put the CPU into RESET, load all or part of the EZUSB
+ * RAM with firmware, then reload the CPUCS register
+ * with ‘0’ to take the CPU out of RESET. The CPUCS register
+ * (at 0xE600) is the only EZ-USB register that can be written
+ * using the Firmware Download command.
+ */
+
+ char value = CPU_RESET;
+ jtag_libusb_control_transfer(libusb_dev,
+ LIBUSB_REQUEST_TYPE_VENDOR | \
+ LIBUSB_ENDPOINT_OUT,
+ USBBLASTER_CTRL_LOAD_FIRM,
+ EZUSB_CPUCS,
+ 0,
+ &value,
+ 1,
+ 100);
+
+ /* Download all sections in the image to ULINK */
+ for (int i = 0; i < ublast2_firmware_image.num_sections; i++) {
+ ret = ublast2_write_firmware_section(libusb_dev,
+ &ublast2_firmware_image, i);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Error while downloading the firmware");
+ return ret;
+ }
+ }
+
+ value = !CPU_RESET;
+ jtag_libusb_control_transfer(libusb_dev,
+ LIBUSB_REQUEST_TYPE_VENDOR | \
+ LIBUSB_ENDPOINT_OUT,
+ USBBLASTER_CTRL_LOAD_FIRM,
+ EZUSB_CPUCS,
+ 0,
+ &value,
+ 1,
+ 100);
+
+ image_close(&ublast2_firmware_image);
+
+ return ERROR_OK;
+}
+
+static int ublast2_libusb_init(struct ublast_lowlevel *low)
+{
+ const uint16_t vids[] = { low->ublast_vid_uninit, 0 };
+ const uint16_t pids[] = { low->ublast_pid_uninit, 0 };
+ struct jtag_libusb_device_handle *temp;
+ bool renumeration = false;
+ int ret;
+
+ if (jtag_libusb_open(vids, pids, &temp) == ERROR_OK) {
+ LOG_INFO("Altera USB-Blaster II (uninitialized) found");
+ LOG_INFO("Loading firmware...");
+ ret = load_usb_blaster_firmware(temp, low);
+ jtag_libusb_close(temp);
+ if (ret != ERROR_OK)
+ return ret;
+ renumeration = true;
+ }
+
+ const uint16_t vids_renum[] = { low->ublast_vid, 0 };
+ const uint16_t pids_renum[] = { low->ublast_pid, 0 };
+
+ if (renumeration == false) {
+ if (jtag_libusb_open(vids_renum, pids_renum, &low->libusb_dev) != ERROR_OK) {
+ LOG_ERROR("Altera USB-Blaster II not found");
+ return ERROR_FAIL;
+ }
+ } else {
+ int retry = 10;
+ while (jtag_libusb_open(vids_renum, pids_renum, &low->libusb_dev) != ERROR_OK && retry--) {
+ usleep(1000000);
+ LOG_INFO("Waiting for renumerate...");
+ }
+
+ if (!retry) {
+ LOG_ERROR("Altera USB-Blaster II not found");
+ return ERROR_FAIL;
+ }
+ }
+
+ char buffer[5];
+ jtag_libusb_control_transfer(low->libusb_dev,
+ LIBUSB_REQUEST_TYPE_VENDOR | \
+ LIBUSB_ENDPOINT_IN,
+ USBBLASTER_CTRL_READ_REV,
+ 0,
+ 0,
+ buffer,
+ 5,
+ 100);
+
+ LOG_INFO("Altera USB-Blaster II found (Firm. rev. = %s)", buffer);
+
+ return ERROR_OK;
+}
+
+static int ublast2_libusb_quit(struct ublast_lowlevel *low)
+{
+ jtag_libusb_close(low->libusb_dev);
+ return ERROR_OK;
+};
+
+static struct ublast_lowlevel low = {
+ .open = ublast2_libusb_init,
+ .close = ublast2_libusb_quit,
+ .read = ublast2_libusb_read,
+ .write = ublast2_libusb_write,
+ .flags = COPY_TDO_BUFFER,
+};
+
+struct ublast_lowlevel *ublast2_register_libusb(void)
+{
+ return &low;
+}
diff --git a/src/jtag/drivers/usb_blaster/ublast_access.h b/src/jtag/drivers/usb_blaster/ublast_access.h
index e0eb059e..9a9762c8 100644
--- a/src/jtag/drivers/usb_blaster/ublast_access.h
+++ b/src/jtag/drivers/usb_blaster/ublast_access.h
@@ -4,6 +4,7 @@
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
+ * Copyright (C) 2013 Franck Jullien franck.jullien@gmail.com
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
@@ -21,10 +22,19 @@
*
*/
+#include <libusb_common.h>
+
+/* Low level flags */
+#define COPY_TDO_BUFFER (1 << 0)
+
struct ublast_lowlevel {
uint16_t ublast_vid;
uint16_t ublast_pid;
+ uint16_t ublast_vid_uninit;
+ uint16_t ublast_pid_uninit;
char *ublast_device_desc;
+ struct jtag_libusb_device_handle *libusb_dev;
+ char *firmware_path;
int (*write)(struct ublast_lowlevel *low, uint8_t *buf, int size,
uint32_t *bytes_written);
@@ -35,18 +45,22 @@ struct ublast_lowlevel {
int (*speed)(struct ublast_lowlevel *low, int speed);
void *priv;
+ int flags;
};
/**
* ublast_register_ftdi - get a lowlevel USB Blaster driver
* ublast_register_ftd2xx - get a lowlevel USB Blaster driver
+ * ublast2_register_libusb - get a lowlevel USB Blaster II driver
*
- * Get a lowlevel USB-Blaster driver. In the current implementation, there are 2
+ * Get a lowlevel USB-Blaster driver. In the current implementation, there are 3
* possible lowlevel drivers :
* - one based on libftdi from ftdichip.com
* - one based on libftdxx, the free alternative
+ * - one based on libusb, specific to the USB-Blaster II
*
* Returns the lowlevel driver structure.
*/
extern struct ublast_lowlevel *ublast_register_ftdi(void);
extern struct ublast_lowlevel *ublast_register_ftd2xx(void);
+extern struct ublast_lowlevel *ublast2_register_libusb(void);
diff --git a/src/jtag/drivers/usb_blaster/usb_blaster.c b/src/jtag/drivers/usb_blaster/usb_blaster.c
index 00a2cd08..1a43f588 100644
--- a/src/jtag/drivers/usb_blaster/usb_blaster.c
+++ b/src/jtag/drivers/usb_blaster/usb_blaster.c
@@ -4,6 +4,7 @@
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
+ * Copyright (C) 2013 Franck Jullien franck.jullien@gmail.com
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
@@ -48,6 +49,16 @@
* | 6 MHz XTAL | | 24 MHz Osc. |
* |_____________| |_____________|
*
+ * USB-JTAG, Altera USB-Blaster II are typically implemented as a Cypress
+ * EZ-USB FX2LP followed by a CPLD.
+ * _____________ _________
+ * | | | |
+ * USB__| EZ-USB FX2 |__| EPM570 |__JTAG (B_TDO,B_TDI,B_TMS,B_TCK)
+ * |_____________| |_________|
+ * __|__________
+ * | |
+ * | 24 MHz XTAL |
+ * |_____________|
*/
#ifdef HAVE_CONFIG_H
@@ -82,6 +93,9 @@
*/
#define BUF_LEN 4096
+/* USB-Blaster II specific command */
+#define CMD_COPY_TDO_BUFFER 0x5F
+
enum gpio_steer {
FIXED_0 = 0,
FIXED_1,
@@ -103,6 +117,9 @@ struct ublast_info {
struct ublast_lowlevel *drv;
char *ublast_device_desc;
uint16_t ublast_vid, ublast_pid;
+ uint16_t ublast_vid_uninit, ublast_pid_uninit;
+ int flags;
+ char *firmware_path;
};
/*
@@ -133,6 +150,9 @@ static struct drvs_map lowlevel_drivers_map[] = {
#if BUILD_USB_BLASTER_FTD2XX
{ .name = "ftd2xx", .drv_register = ublast_register_ftd2xx },
#endif
+#if BUILD_USB_BLASTER_2
+ { .name = "ublast2", .drv_register = ublast2_register_libusb },
+#endif
{ NULL, NULL },
};
@@ -631,8 +651,11 @@ static void ublast_queue_tdi(uint8_t *bits, int nb_bits, enum scan_type scan)
ublast_queue_bytes(&bits[i], trans);
else
ublast_queue_bytes(byte0, trans);
- if (read_tdos)
+ if (read_tdos) {
+ if (info.flags & COPY_TDO_BUFFER)
+ ublast_queue_byte(CMD_COPY_TDO_BUFFER);
ublast_read_byteshifted_tdos(&tdos[i], trans);
+ }
}
/*
@@ -645,8 +668,11 @@ static void ublast_queue_tdi(uint8_t *bits, int nb_bits, enum scan_type scan)
else
ublast_clock_tdi(tdi, scan);
}
- if (nb1 && read_tdos)
+ if (nb1 && read_tdos) {
+ if (info.flags & COPY_TDO_BUFFER)
+ ublast_queue_byte(CMD_COPY_TDO_BUFFER);
ublast_read_bitbang_tdos(&tdos[nb8], nb1);
+ }
if (bits)
memcpy(bits, tdos, DIV_ROUND_UP(nb_bits, 8));
@@ -804,6 +830,7 @@ static int ublast_init(void)
LOG_ERROR("no lowlevel driver found");
return ERROR_JTAG_DEVICE_ERROR;
}
+ info.lowlevel_name = strdup(lowlevel_drivers_map[i-1].name);
}
/*
@@ -811,7 +838,12 @@ static int ublast_init(void)
*/
info.drv->ublast_vid = info.ublast_vid;
info.drv->ublast_pid = info.ublast_pid;
+ info.drv->ublast_vid_uninit = info.ublast_vid_uninit;
+ info.drv->ublast_pid_uninit = info.ublast_pid_uninit;
info.drv->ublast_device_desc = info.ublast_device_desc;
+ info.drv->firmware_path = info.firmware_path;
+
+ info.flags |= info.drv->flags;
ret = info.drv->open(info.drv);
if (ret == ERROR_OK) {
@@ -860,18 +892,26 @@ COMMAND_HANDLER(ublast_handle_device_desc_command)
COMMAND_HANDLER(ublast_handle_vid_pid_command)
{
- if (CMD_ARGC > 2) {
+ if (CMD_ARGC > 4) {
LOG_WARNING("ignoring extra IDs in ublast_vid_pid "
- "(maximum is 1 pair)");
- CMD_ARGC = 2;
+ "(maximum is 2 pairs)");
+ CMD_ARGC = 4;
}
- if (CMD_ARGC == 2) {
+
+ if (CMD_ARGC >= 2) {
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], info.ublast_vid);
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], info.ublast_pid);
} else {
LOG_WARNING("incomplete ublast_vid_pid configuration");
}
+ if (CMD_ARGC == 4) {
+ COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], info.ublast_vid_uninit);
+ COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], info.ublast_pid_uninit);
+ } else {
+ LOG_WARNING("incomplete ublast_vid_pid configuration");
+ }
+
return ERROR_OK;
}
@@ -951,6 +991,18 @@ COMMAND_HANDLER(ublast_handle_lowlevel_drv_command)
return ERROR_OK;
}
+COMMAND_HANDLER(ublast_firmware_command)
+{
+ if (CMD_ARGC == 1)
+ info.firmware_path = strdup(CMD_ARGV[0]);
+ else
+ LOG_ERROR("require exactly one argument to "
+ "ublast_firmware_command <path>");
+
+ return ERROR_OK;
+}
+
+
static const struct command_registration ublast_command_handlers[] = {
{
.name = "usb_blaster_device_desc",
@@ -963,15 +1015,17 @@ static const struct command_registration ublast_command_handlers[] = {
.name = "usb_blaster_vid_pid",
.handler = ublast_handle_vid_pid_command,
.mode = COMMAND_CONFIG,
- .help = "the vendor ID and product ID of the USB-Blaster",
- .usage = "vid pid",
+ .help = "the vendor ID and product ID of the USB-Blaster and " \
+ "vendor ID and product ID of the uninitialized device " \
+ "for USB-Blaster II",
+ .usage = "vid pid vid_uninit pid_uninit",
},
{
.name = "usb_blaster_lowlevel_driver",
.handler = ublast_handle_lowlevel_drv_command,
.mode = COMMAND_CONFIG,
- .help = "set the lowlevel access for the USB Blaster (ftdi, ftd2xx)",
- .usage = "(ftdi|ftd2xx)",
+ .help = "set the lowlevel access for the USB Blaster (ftdi, ftd2xx, ublast2)",
+ .usage = "(ftdi|ftd2xx|ublast2)",
},
{
.name = "usb_blaster_pin",
@@ -980,6 +1034,13 @@ static const struct command_registration ublast_command_handlers[] = {
.help = "show or set pin state for the unused GPIO pins",
.usage = "(pin6|pin8) (0|1|s|t)",
},
+ {
+ .name = "usb_blaster_firmware",
+ .handler = &ublast_firmware_command,
+ .mode = COMMAND_CONFIG,
+ .help = "configure the USB-Blaster II firmware location",
+ .usage = "path/to/blaster_xxxx.hex",
+ },
COMMAND_REGISTRATION_DONE
};