|
|
@@ -0,0 +1,845 @@ |
|
|
|
/******************************************************************************* |
|
|
|
* Driver for OpenJTAG Project (www.openjtag.org) * |
|
|
|
* Compatible with libftdi and ftd2xx drivers. * |
|
|
|
* * |
|
|
|
* Copyright (C) 2010 by Ivan Meleca <mileca@gmail.com> * |
|
|
|
* * |
|
|
|
* Copyright (C) 2013 by Ryan Corbin, GlueLogix Inc. <corbin.ryan@gmail.com> * |
|
|
|
* Updated to work with OpenOCD v0.7.0. Fixed libftdi read speed issue. * |
|
|
|
* * |
|
|
|
* Based on usb_blaster.c * |
|
|
|
* Copyright (C) 2009 Catalin Patulea * |
|
|
|
* Copyright (C) 2006 Kolja Waschk * |
|
|
|
* * |
|
|
|
* And jlink.c * |
|
|
|
* Copyright (C) 2008 by Spencer Oliver * |
|
|
|
* spen@spen-soft.co.uk * |
|
|
|
* * |
|
|
|
* 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., * |
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * |
|
|
|
***************************************************************************/ |
|
|
|
|
|
|
|
/*************************************************************************** |
|
|
|
* Version 1.0 Tested on a MCBSTM32 board using a Cortex M3 (stm32f103x), * |
|
|
|
* GDB and Eclipse under Linux (Ubuntu 10.04) * |
|
|
|
* * |
|
|
|
***************************************************************************/ |
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H |
|
|
|
#include "config.h" |
|
|
|
#endif |
|
|
|
|
|
|
|
#include <jtag/interface.h> |
|
|
|
#include <jtag/commands.h> |
|
|
|
#include "usb_common.h" |
|
|
|
|
|
|
|
/* |
|
|
|
* OpenJTAG-OpenOCD state conversion |
|
|
|
*/ |
|
|
|
typedef enum openjtag_tap_state { |
|
|
|
OPENJTAG_TAP_INVALID = -1, |
|
|
|
OPENJTAG_TAP_RESET = 0, |
|
|
|
OPENJTAG_TAP_IDLE = 1, |
|
|
|
OPENJTAG_TAP_SELECT_DR = 2, |
|
|
|
OPENJTAG_TAP_CAPTURE_DR = 3, |
|
|
|
OPENJTAG_TAP_SHIFT_DR = 4, |
|
|
|
OPENJTAG_TAP_EXIT1_DR = 5, |
|
|
|
OPENJTAG_TAP_PAUSE_DR = 6, |
|
|
|
OPENJTAG_TAP_EXIT2_DR = 7, |
|
|
|
OPENJTAG_TAP_UPDATE_DR = 8, |
|
|
|
OPENJTAG_TAP_SELECT_IR = 9, |
|
|
|
OPENJTAG_TAP_CAPURE_IR = 10, |
|
|
|
OPENJTAG_TAP_SHIFT_IR = 11, |
|
|
|
OPENJTAG_TAP_EXIT1_IR = 12, |
|
|
|
OPENJTAG_TAP_PAUSE_IR = 13, |
|
|
|
OPENJTAG_TAP_EXIT2_IR = 14, |
|
|
|
OPENJTAG_TAP_UPDATE_IR = 15, |
|
|
|
} openjtag_tap_state_t; |
|
|
|
|
|
|
|
#if (BUILD_OPENJTAG_FTD2XX == 1 && BUILD_OPENJTAG_LIBFTDI == 1) |
|
|
|
#error "BUILD_OPENJTAG_FTD2XX && BUILD_OPENJTAG_LIBFTDI " |
|
|
|
"are mutually exclusive" |
|
|
|
#elif (BUILD_OPENJTAG_FTD2XX != 1 && BUILD_OPENJTAG_LIBFTDI != 1) |
|
|
|
#error "BUILD_OPENJTAG_FTD2XX || BUILD_OPENJTAG_LIBFTDI must be chosen" |
|
|
|
#endif |
|
|
|
|
|
|
|
/* OPENJTAG access library includes */ |
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
#include <ftd2xx.h> |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
#include <ftdi.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
/* OpenJTAG vid/pid */ |
|
|
|
static uint16_t openjtag_vid = 0x0403; |
|
|
|
static uint16_t openjtag_pid = 0x6001; |
|
|
|
|
|
|
|
static char *openjtag_device_desc; |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
static FT_HANDLE ftdih; |
|
|
|
|
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
static struct ftdi_context ftdic; |
|
|
|
#endif |
|
|
|
|
|
|
|
#define OPENJTAG_BUFFER_SIZE 504 |
|
|
|
#define OPENJTAG_MAX_PENDING_RESULTS 256 |
|
|
|
|
|
|
|
struct openjtag_scan_result { |
|
|
|
uint32_t bits; /* Length in bits*/ |
|
|
|
struct scan_command *command; /* Corresponding scan command */ |
|
|
|
uint8_t *buffer; |
|
|
|
}; |
|
|
|
|
|
|
|
/* USB RX/TX buffers */ |
|
|
|
static int usb_tx_buf_offs; |
|
|
|
static uint8_t usb_tx_buf[OPENJTAG_BUFFER_SIZE]; |
|
|
|
static uint32_t usb_rx_buf_len; |
|
|
|
static uint8_t usb_rx_buf[OPENJTAG_BUFFER_SIZE]; |
|
|
|
|
|
|
|
/* Pending readings */ |
|
|
|
static struct openjtag_scan_result openjtag_scan_result_buffer[OPENJTAG_MAX_PENDING_RESULTS]; |
|
|
|
static int openjtag_scan_result_count; |
|
|
|
|
|
|
|
/* Openocd usb handler */ |
|
|
|
struct openocd { |
|
|
|
struct usb_dev_handle *usb_handle; |
|
|
|
}; |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
|
|
|
|
#define DEBUG_TYPE_READ 0 |
|
|
|
#define DEBUG_TYPE_WRITE 1 |
|
|
|
#define DEBUG_TYPE_OCD_READ 2 |
|
|
|
#define DEBUG_TYPE_BUFFER 3 |
|
|
|
|
|
|
|
#define LINE_LEN 16 |
|
|
|
static void openjtag_debug_buffer(uint8_t *buffer, int length, uint8_t type) |
|
|
|
{ |
|
|
|
char line[128]; |
|
|
|
char s[4]; |
|
|
|
int i; |
|
|
|
int j; |
|
|
|
|
|
|
|
switch (type) { |
|
|
|
case DEBUG_TYPE_READ: |
|
|
|
sprintf(line, "USB READ %d bytes", length); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_WRITE: |
|
|
|
sprintf(line, "USB WRITE %d bytes", length); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_OCD_READ: |
|
|
|
sprintf(line, "TO OpenOCD %d bytes", length); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_BUFFER: |
|
|
|
sprintf(line, "Buffer %d bytes", length); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
LOG_DEBUG("%s", line); |
|
|
|
|
|
|
|
for (i = 0; i < length; i += LINE_LEN) { |
|
|
|
switch (type) { |
|
|
|
case DEBUG_TYPE_READ: |
|
|
|
sprintf(line, "USB READ: %04x", i); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_WRITE: |
|
|
|
sprintf(line, "USB WRITE: %04x", i); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_OCD_READ: |
|
|
|
sprintf(line, "TO OpenOCD: %04x", i); |
|
|
|
break; |
|
|
|
case DEBUG_TYPE_BUFFER: |
|
|
|
sprintf(line, "BUFFER: %04x", i); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
for (j = i; j < i + LINE_LEN && j < length; j++) { |
|
|
|
sprintf(s, " %02x", buffer[j]); |
|
|
|
strcat(line, s); |
|
|
|
} |
|
|
|
LOG_DEBUG("%s", line); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
static int8_t openjtag_get_tap_state(int8_t state) |
|
|
|
{ |
|
|
|
|
|
|
|
switch (state) { |
|
|
|
case TAP_DREXIT2: return OPENJTAG_TAP_EXIT2_DR; |
|
|
|
case TAP_DREXIT1: return OPENJTAG_TAP_EXIT1_DR; |
|
|
|
case TAP_DRSHIFT: return OPENJTAG_TAP_SHIFT_DR; |
|
|
|
case TAP_DRPAUSE: return OPENJTAG_TAP_PAUSE_DR; |
|
|
|
case TAP_IRSELECT: return OPENJTAG_TAP_SELECT_IR; |
|
|
|
case TAP_DRUPDATE: return OPENJTAG_TAP_UPDATE_DR; |
|
|
|
case TAP_DRCAPTURE: return OPENJTAG_TAP_CAPTURE_DR; |
|
|
|
case TAP_DRSELECT: return OPENJTAG_TAP_SELECT_DR; |
|
|
|
case TAP_IREXIT2: return OPENJTAG_TAP_EXIT2_IR; |
|
|
|
case TAP_IREXIT1: return OPENJTAG_TAP_EXIT1_IR; |
|
|
|
case TAP_IRSHIFT: return OPENJTAG_TAP_SHIFT_IR; |
|
|
|
case TAP_IRPAUSE: return OPENJTAG_TAP_PAUSE_IR; |
|
|
|
case TAP_IDLE: return OPENJTAG_TAP_IDLE; |
|
|
|
case TAP_IRUPDATE: return OPENJTAG_TAP_UPDATE_IR; |
|
|
|
case TAP_IRCAPTURE: return OPENJTAG_TAP_CAPURE_IR; |
|
|
|
case TAP_RESET: return OPENJTAG_TAP_RESET; |
|
|
|
case TAP_INVALID: |
|
|
|
default: return OPENJTAG_TAP_INVALID; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_buf_write( |
|
|
|
uint8_t *buf, int size, uint32_t *bytes_written) |
|
|
|
{ |
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
FT_STATUS status; |
|
|
|
DWORD dw_bytes_written; |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buf, size, DEBUG_TYPE_WRITE); |
|
|
|
#endif |
|
|
|
|
|
|
|
status = FT_Write(ftdih, buf, size, &dw_bytes_written); |
|
|
|
if (status != FT_OK) { |
|
|
|
*bytes_written = dw_bytes_written; |
|
|
|
LOG_ERROR("FT_Write returned: %u", status); |
|
|
|
return ERROR_JTAG_DEVICE_ERROR; |
|
|
|
} |
|
|
|
*bytes_written = dw_bytes_written; |
|
|
|
return ERROR_OK; |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
int retval; |
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buf, size, DEBUG_TYPE_WRITE); |
|
|
|
#endif |
|
|
|
|
|
|
|
retval = ftdi_write_data(&ftdic, buf, size); |
|
|
|
if (retval < 0) { |
|
|
|
*bytes_written = 0; |
|
|
|
LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(&ftdic)); |
|
|
|
return ERROR_JTAG_DEVICE_ERROR; |
|
|
|
} |
|
|
|
|
|
|
|
*bytes_written += retval; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_buf_read(uint8_t *buf, uint32_t qty, uint32_t *bytes_read) |
|
|
|
{ |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
DWORD dw_bytes_read; |
|
|
|
FT_STATUS status; |
|
|
|
int timeout = 50; |
|
|
|
|
|
|
|
*bytes_read = 0; |
|
|
|
while (qty && (*bytes_read < qty) && timeout--) { |
|
|
|
|
|
|
|
status = FT_Read(ftdih, buf + *bytes_read, |
|
|
|
qty - *bytes_read, &dw_bytes_read); |
|
|
|
if (status != FT_OK) { |
|
|
|
*bytes_read = dw_bytes_read; |
|
|
|
LOG_ERROR("FT_Read returned: %u", status); |
|
|
|
return ERROR_JTAG_DEVICE_ERROR; |
|
|
|
} |
|
|
|
*bytes_read += dw_bytes_read; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buf, *bytes_read, DEBUG_TYPE_READ); |
|
|
|
#endif |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
int retval; |
|
|
|
int timeout = 5; |
|
|
|
|
|
|
|
*bytes_read = 0; |
|
|
|
|
|
|
|
while ((*bytes_read < qty) && timeout--) { |
|
|
|
retval = ftdi_read_data(&ftdic, buf + *bytes_read, |
|
|
|
qty - *bytes_read); |
|
|
|
if (retval < 0) { |
|
|
|
*bytes_read = 0; |
|
|
|
DEBUG_JTAG_IO("ftdi_read_data: %s", |
|
|
|
ftdi_get_error_string(&ftdic)); |
|
|
|
return ERROR_JTAG_DEVICE_ERROR; |
|
|
|
} |
|
|
|
*bytes_read += retval; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buf, *bytes_read, DEBUG_TYPE_READ); |
|
|
|
#endif |
|
|
|
|
|
|
|
#endif |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_sendcommand(uint8_t cmd) |
|
|
|
{ |
|
|
|
uint32_t written; |
|
|
|
return openjtag_buf_write(&cmd, 1, &written); |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_speed(int speed) |
|
|
|
{ |
|
|
|
int clockcmd; |
|
|
|
switch (speed) { |
|
|
|
case 48000: |
|
|
|
clockcmd = 0x00; |
|
|
|
break; |
|
|
|
case 24000: |
|
|
|
clockcmd = 0x20; |
|
|
|
break; |
|
|
|
case 12000: |
|
|
|
clockcmd = 0x40; |
|
|
|
break; |
|
|
|
case 6000: |
|
|
|
clockcmd = 0x60; |
|
|
|
break; |
|
|
|
case 3000: |
|
|
|
clockcmd = 0x80; |
|
|
|
break; |
|
|
|
case 1500: |
|
|
|
clockcmd = 0xA0; |
|
|
|
break; |
|
|
|
case 750: |
|
|
|
clockcmd = 0xC0; |
|
|
|
break; |
|
|
|
case 375: |
|
|
|
clockcmd = 0xE0; |
|
|
|
break; |
|
|
|
default: |
|
|
|
clockcmd = 0xE0; |
|
|
|
LOG_WARNING("adapter speed not recognized, reverting to 375 kHz"); |
|
|
|
break; |
|
|
|
} |
|
|
|
openjtag_sendcommand(clockcmd); |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_init(void) |
|
|
|
{ |
|
|
|
uint8_t latency_timer; |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
FT_STATUS status; |
|
|
|
#endif |
|
|
|
|
|
|
|
usb_tx_buf_offs = 0; |
|
|
|
usb_rx_buf_len = 0; |
|
|
|
openjtag_scan_result_count = 0; |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
LOG_DEBUG("'openjtag' interface using FTD2XX"); |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
LOG_DEBUG("'openjtag' interface using libftdi"); |
|
|
|
#endif |
|
|
|
|
|
|
|
/* Open by device description */ |
|
|
|
if (openjtag_device_desc == NULL) { |
|
|
|
LOG_WARNING("no openjtag device description specified, " |
|
|
|
"using default 'Open JTAG Project'"); |
|
|
|
openjtag_device_desc = "Open JTAG Project"; |
|
|
|
} |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
|
|
|
|
#if IS_WIN32 == 0 |
|
|
|
/* Add non-standard Vid/Pid to the linux driver */ |
|
|
|
status = FT_SetVIDPID(openjtag_vid, openjtag_pid); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_WARNING("couldn't add %4.4x:%4.4x", |
|
|
|
openjtag_vid, openjtag_pid); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
status = FT_OpenEx(openjtag_device_desc, FT_OPEN_BY_DESCRIPTION, |
|
|
|
&ftdih); |
|
|
|
if (status != FT_OK) { |
|
|
|
DWORD num_devices; |
|
|
|
|
|
|
|
LOG_ERROR("unable to open ftdi device: %u", status); |
|
|
|
status = FT_ListDevices(&num_devices, NULL, |
|
|
|
FT_LIST_NUMBER_ONLY); |
|
|
|
if (status == FT_OK) { |
|
|
|
char **desc_array = malloc(sizeof(char *) |
|
|
|
* (num_devices + 1)); |
|
|
|
unsigned int i; |
|
|
|
|
|
|
|
for (i = 0; i < num_devices; i++) |
|
|
|
desc_array[i] = malloc(64); |
|
|
|
desc_array[num_devices] = NULL; |
|
|
|
|
|
|
|
status = FT_ListDevices(desc_array, &num_devices, |
|
|
|
FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION); |
|
|
|
|
|
|
|
if (status == FT_OK) { |
|
|
|
LOG_ERROR("ListDevices: %u\n", num_devices); |
|
|
|
for (i = 0; i < num_devices; i++) |
|
|
|
LOG_ERROR("%i: %s", i, desc_array[i]); |
|
|
|
} |
|
|
|
|
|
|
|
for (i = 0; i < num_devices; i++) |
|
|
|
free(desc_array[i]); |
|
|
|
free(desc_array); |
|
|
|
} else { |
|
|
|
LOG_ERROR("ListDevices: NONE\n"); |
|
|
|
} |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
status = FT_SetLatencyTimer(ftdih, 2); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_ERROR("unable to set latency timer: %u", status); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
status = FT_GetLatencyTimer(ftdih, &latency_timer); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_ERROR("unable to get latency timer: %u", status); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
LOG_DEBUG("current latency timer: %i", latency_timer); |
|
|
|
|
|
|
|
status = FT_SetBitMode(ftdih, 0x00, 0x40); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_ERROR("unable to disable bit i/o mode: %u", status); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
status = FT_SetTimeouts(ftdih, 50, 0); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_ERROR("unable to set timeouts: %u", status); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX); |
|
|
|
if (status != FT_OK) { |
|
|
|
LOG_ERROR("unable to FT_Purge() %u", status); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
if (ftdi_init(&ftdic) < 0) |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
|
|
|
|
/* context, vendor id, product id, description, serial id */ |
|
|
|
if (ftdi_usb_open_desc(&ftdic, openjtag_vid, openjtag_pid, openjtag_device_desc, NULL) < 0) { |
|
|
|
LOG_ERROR("unable to open ftdi device: %s", ftdic.error_str); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
if (ftdi_usb_reset(&ftdic) < 0) { |
|
|
|
LOG_ERROR("unable to reset ftdi device"); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
if (ftdi_set_latency_timer(&ftdic, 2) < 0) { |
|
|
|
LOG_ERROR("unable to set latency timer"); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
if (ftdi_get_latency_timer(&ftdic, &latency_timer) < 0) { |
|
|
|
LOG_ERROR("unable to get latency timer"); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
LOG_DEBUG("current latency timer: %u", latency_timer); |
|
|
|
|
|
|
|
ftdi_disable_bitbang(&ftdic); |
|
|
|
/* was (3000000 / 4) with a comment about a bug in libftdi when using high baudrate */ |
|
|
|
if (ftdi_set_baudrate(&ftdic, 3000000) < 0) { |
|
|
|
LOG_ERROR("Can't set baud rate to max: %s", |
|
|
|
ftdi_get_error_string(&ftdic)); |
|
|
|
return ERROR_JTAG_DEVICE_ERROR; |
|
|
|
}; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX); |
|
|
|
if (status != FT_OK) |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
if (ftdi_usb_purge_buffers(&ftdic) < 0) { |
|
|
|
LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str); |
|
|
|
return ERROR_JTAG_INIT_FAILED; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/* OpenJTAG speed */ |
|
|
|
openjtag_sendcommand(0xE0); /*Start at slowest adapter speed*/ |
|
|
|
|
|
|
|
/* MSB */ |
|
|
|
openjtag_sendcommand(0x75); |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_quit(void) |
|
|
|
{ |
|
|
|
#if BUILD_OPENJTAG_FTD2XX == 1 |
|
|
|
FT_Close(ftdih); |
|
|
|
#elif BUILD_OPENJTAG_LIBFTDI == 1 |
|
|
|
ftdi_usb_close(&ftdic); |
|
|
|
ftdi_deinit(&ftdic); |
|
|
|
#endif |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_write_tap_buffer(void) |
|
|
|
{ |
|
|
|
uint32_t written; |
|
|
|
|
|
|
|
openjtag_buf_write(usb_tx_buf, usb_tx_buf_offs, &written); |
|
|
|
openjtag_buf_read(usb_rx_buf, usb_tx_buf_offs, &usb_rx_buf_len); |
|
|
|
|
|
|
|
usb_tx_buf_offs = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_execute_tap_queue(void) |
|
|
|
{ |
|
|
|
openjtag_write_tap_buffer(); |
|
|
|
|
|
|
|
int res_count = 0; |
|
|
|
|
|
|
|
if (openjtag_scan_result_count && usb_rx_buf_len) { |
|
|
|
|
|
|
|
int count; |
|
|
|
int rx_offs = 0; |
|
|
|
int len; |
|
|
|
|
|
|
|
/* for every pending result */ |
|
|
|
while (res_count < openjtag_scan_result_count) { |
|
|
|
|
|
|
|
/* get sent bits */ |
|
|
|
len = openjtag_scan_result_buffer[res_count].bits; |
|
|
|
|
|
|
|
count = 0; |
|
|
|
|
|
|
|
uint8_t *buffer = openjtag_scan_result_buffer[res_count].buffer; |
|
|
|
|
|
|
|
while (len) { |
|
|
|
if (len <= 8) { |
|
|
|
DEBUG_JTAG_IO("bits < 8 buf = 0x%X, will be 0x%X", |
|
|
|
usb_rx_buf[rx_offs], usb_rx_buf[rx_offs] >> (8 - len)); |
|
|
|
buffer[count] = usb_rx_buf[rx_offs] >> (8 - len); |
|
|
|
len = 0; |
|
|
|
} else { |
|
|
|
buffer[count] = usb_rx_buf[rx_offs]; |
|
|
|
len -= 8; |
|
|
|
} |
|
|
|
|
|
|
|
rx_offs++; |
|
|
|
count++; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buffer, |
|
|
|
DIV_ROUND_UP(openjtag_scan_result_buffer[res_count].bits, 8), DEBUG_TYPE_OCD_READ); |
|
|
|
#endif |
|
|
|
jtag_read_buffer(buffer, openjtag_scan_result_buffer[res_count].command); |
|
|
|
|
|
|
|
if (openjtag_scan_result_buffer[res_count].buffer) |
|
|
|
free(openjtag_scan_result_buffer[res_count].buffer); |
|
|
|
|
|
|
|
res_count++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
openjtag_scan_result_count = 0; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_add_byte(char buf) |
|
|
|
{ |
|
|
|
|
|
|
|
if (usb_tx_buf_offs == OPENJTAG_BUFFER_SIZE) { |
|
|
|
DEBUG_JTAG_IO("Forcing execute_tap_queue"); |
|
|
|
DEBUG_JTAG_IO("TX Buff offs=%d", usb_tx_buf_offs); |
|
|
|
openjtag_execute_tap_queue(); |
|
|
|
} |
|
|
|
|
|
|
|
usb_tx_buf[usb_tx_buf_offs] = buf; |
|
|
|
usb_tx_buf_offs++; |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_add_scan(uint8_t *buffer, int length, struct scan_command *scan_cmd) |
|
|
|
{ |
|
|
|
|
|
|
|
/* Ensure space to send long chains */ |
|
|
|
/* We add two byte for each eight (or less) bits, one for command, one for data */ |
|
|
|
if (usb_tx_buf_offs + (DIV_ROUND_UP(length, 8) * 2) >= OPENJTAG_BUFFER_SIZE) { |
|
|
|
DEBUG_JTAG_IO("Forcing execute_tap_queue from scan"); |
|
|
|
DEBUG_JTAG_IO("TX Buff offs=%d len=%d", usb_tx_buf_offs, DIV_ROUND_UP(length, 8) * 2); |
|
|
|
openjtag_execute_tap_queue(); |
|
|
|
} |
|
|
|
|
|
|
|
openjtag_scan_result_buffer[openjtag_scan_result_count].bits = length; |
|
|
|
openjtag_scan_result_buffer[openjtag_scan_result_count].command = scan_cmd; |
|
|
|
openjtag_scan_result_buffer[openjtag_scan_result_count].buffer = buffer; |
|
|
|
|
|
|
|
uint8_t command; |
|
|
|
uint8_t bits; |
|
|
|
int count = 0; |
|
|
|
while (length) { |
|
|
|
|
|
|
|
/* write command */ |
|
|
|
command = 6; |
|
|
|
|
|
|
|
/* last bits? */ |
|
|
|
if (length <= 8) { |
|
|
|
/* tms high */ |
|
|
|
command |= (1 << 4); |
|
|
|
|
|
|
|
/* bits to transfer */ |
|
|
|
bits = (length - 1); |
|
|
|
command |= bits << 5; |
|
|
|
length = 0; |
|
|
|
} else { |
|
|
|
/* whole byte */ |
|
|
|
|
|
|
|
/* bits to transfer */ |
|
|
|
bits = 7; |
|
|
|
command |= (7 << 5); |
|
|
|
length -= 8; |
|
|
|
} |
|
|
|
|
|
|
|
openjtag_add_byte(command); |
|
|
|
openjtag_add_byte(buffer[count]); |
|
|
|
count++; |
|
|
|
} |
|
|
|
|
|
|
|
openjtag_scan_result_count++; |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_execute_reset(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
|
|
|
|
DEBUG_JTAG_IO("reset trst: %i srst %i", |
|
|
|
cmd->cmd.reset->trst, cmd->cmd.reset->srst); |
|
|
|
|
|
|
|
uint8_t buf = 0x00; |
|
|
|
|
|
|
|
if (cmd->cmd.reset->trst) { |
|
|
|
buf = 0x03; |
|
|
|
} else { |
|
|
|
buf |= 0x04; |
|
|
|
buf |= 0x05 << 4; |
|
|
|
} |
|
|
|
|
|
|
|
openjtag_add_byte(buf); |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_execute_sleep(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
jtag_sleep(cmd->cmd.sleep->us); |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_set_state(uint8_t openocd_state) |
|
|
|
{ |
|
|
|
int8_t state = openjtag_get_tap_state(openocd_state); |
|
|
|
|
|
|
|
uint8_t buf = 0; |
|
|
|
buf = 0x01; |
|
|
|
buf |= state << 4; |
|
|
|
|
|
|
|
openjtag_add_byte(buf); |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_execute_statemove(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
DEBUG_JTAG_IO("state move to %i", cmd->cmd.statemove->end_state); |
|
|
|
|
|
|
|
tap_set_end_state(cmd->cmd.statemove->end_state); |
|
|
|
|
|
|
|
openjtag_set_state(cmd->cmd.statemove->end_state); |
|
|
|
|
|
|
|
tap_set_state(tap_get_end_state()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void openjtag_execute_scan(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
|
|
|
|
int scan_size, old_state; |
|
|
|
uint8_t *buffer; |
|
|
|
|
|
|
|
DEBUG_JTAG_IO("scan ends in %s", tap_state_name(cmd->cmd.scan->end_state)); |
|
|
|
|
|
|
|
/* get scan info */ |
|
|
|
tap_set_end_state(cmd->cmd.scan->end_state); |
|
|
|
scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); |
|
|
|
|
|
|
|
#ifdef _DEBUG_USB_COMMS_ |
|
|
|
openjtag_debug_buffer(buffer, (scan_size + 7) / 8, DEBUG_TYPE_BUFFER); |
|
|
|
#endif |
|
|
|
/* set state */ |
|
|
|
old_state = tap_get_end_state(); |
|
|
|
openjtag_set_state(cmd->cmd.scan->ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT); |
|
|
|
tap_set_state(cmd->cmd.scan->ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT); |
|
|
|
tap_set_end_state(old_state); |
|
|
|
|
|
|
|
openjtag_add_scan(buffer, scan_size, cmd->cmd.scan); |
|
|
|
|
|
|
|
openjtag_set_state(cmd->cmd.scan->ir_scan ? TAP_IRPAUSE : TAP_DRPAUSE); |
|
|
|
tap_set_state(cmd->cmd.scan->ir_scan ? TAP_IRPAUSE : TAP_DRPAUSE); |
|
|
|
|
|
|
|
if (tap_get_state() != tap_get_end_state()) { |
|
|
|
openjtag_set_state(tap_get_end_state()); |
|
|
|
tap_set_state(tap_get_end_state()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_execute_runtest(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
|
|
|
|
tap_state_t end_state = cmd->cmd.runtest->end_state; |
|
|
|
tap_set_end_state(end_state); |
|
|
|
|
|
|
|
/* only do a state_move when we're not already in IDLE */ |
|
|
|
if (tap_get_state() != TAP_IDLE) { |
|
|
|
openjtag_set_state(TAP_IDLE); |
|
|
|
tap_set_state(TAP_IDLE); |
|
|
|
} |
|
|
|
|
|
|
|
if (cmd->cmd.runtest->num_cycles > 16) |
|
|
|
LOG_WARNING("num_cycles > 16 on run test"); |
|
|
|
|
|
|
|
uint8_t command; |
|
|
|
command = 7; |
|
|
|
command |= ((cmd->cmd.runtest->num_cycles - 1) & 0x0F) << 4; |
|
|
|
|
|
|
|
openjtag_add_byte(command); |
|
|
|
|
|
|
|
tap_set_end_state(end_state); |
|
|
|
if (tap_get_end_state() != tap_get_state()) { |
|
|
|
openjtag_set_state(end_state); |
|
|
|
tap_set_state(end_state); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void openjtag_execute_command(struct jtag_command *cmd) |
|
|
|
{ |
|
|
|
DEBUG_JTAG_IO("openjtag_execute_command %i", cmd->type); |
|
|
|
switch (cmd->type) { |
|
|
|
case JTAG_RESET: |
|
|
|
openjtag_execute_reset(cmd); |
|
|
|
break; |
|
|
|
case JTAG_SLEEP: |
|
|
|
openjtag_execute_sleep(cmd); |
|
|
|
break; |
|
|
|
case JTAG_TLR_RESET: |
|
|
|
openjtag_execute_statemove(cmd); |
|
|
|
break; |
|
|
|
case JTAG_SCAN: |
|
|
|
openjtag_execute_scan(cmd); |
|
|
|
break; |
|
|
|
case JTAG_RUNTEST: |
|
|
|
openjtag_execute_runtest(cmd); |
|
|
|
break; |
|
|
|
case JTAG_PATHMOVE: |
|
|
|
/* jlink_execute_pathmove(cmd); break; */ |
|
|
|
default: |
|
|
|
LOG_ERROR("BUG: unknown Open JTAG command type encountered"); |
|
|
|
exit(-1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_execute_queue(void) |
|
|
|
{ |
|
|
|
struct jtag_command *cmd = jtag_command_queue; |
|
|
|
|
|
|
|
while (cmd != NULL) { |
|
|
|
openjtag_execute_command(cmd); |
|
|
|
cmd = cmd->next; |
|
|
|
} |
|
|
|
|
|
|
|
return openjtag_execute_tap_queue(); |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_speed_div(int speed, int *khz) |
|
|
|
{ |
|
|
|
*khz = speed; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
static int openjtag_khz(int khz, int *jtag_speed) |
|
|
|
{ |
|
|
|
|
|
|
|
if (khz >= 48000) |
|
|
|
*jtag_speed = 48000; |
|
|
|
else if (khz >= 24000) |
|
|
|
*jtag_speed = 24000; |
|
|
|
else if (khz >= 12000) |
|
|
|
*jtag_speed = 12000; |
|
|
|
else if (khz >= 6000) |
|
|
|
*jtag_speed = 6000; |
|
|
|
else if (khz >= 3000) |
|
|
|
*jtag_speed = 3000; |
|
|
|
else if (khz >= 1500) |
|
|
|
*jtag_speed = 1500; |
|
|
|
else if (khz >= 750) |
|
|
|
*jtag_speed = 750; |
|
|
|
else |
|
|
|
*jtag_speed = 375; |
|
|
|
|
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
COMMAND_HANDLER(openjtag_handle_device_desc_command) |
|
|
|
{ |
|
|
|
if (CMD_ARGC == 1) |
|
|
|
openjtag_device_desc = strdup(CMD_ARGV[0]); |
|
|
|
else |
|
|
|
LOG_ERROR("require exactly one argument to " |
|
|
|
"openjtag_device_desc <description>"); |
|
|
|
return ERROR_OK; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static const struct command_registration openjtag_command_handlers[] = { |
|
|
|
{ |
|
|
|
.name = "openjtag_device_desc", |
|
|
|
.handler = openjtag_handle_device_desc_command, |
|
|
|
.mode = COMMAND_CONFIG, |
|
|
|
.help = "set the USB device description of the OpenJTAG", |
|
|
|
.usage = "description-string", |
|
|
|
}, |
|
|
|
COMMAND_REGISTRATION_DONE |
|
|
|
}; |
|
|
|
|
|
|
|
struct jtag_interface openjtag_interface = { |
|
|
|
.name = "openjtag", |
|
|
|
.commands = openjtag_command_handlers, |
|
|
|
|
|
|
|
.execute_queue = openjtag_execute_queue, |
|
|
|
.speed = openjtag_speed, |
|
|
|
.speed_div = openjtag_speed_div, |
|
|
|
.khz = openjtag_khz, |
|
|
|
.init = openjtag_init, |
|
|
|
.quit = openjtag_quit, |
|
|
|
}; |
|
|
|
|
|
|
|
|