From f6449a7cba11de589c40169a7dd3b183bd60d1f4 Mon Sep 17 00:00:00 2001 From: Forest Crossman Date: Sat, 30 Jan 2016 00:23:49 -0500 Subject: [PATCH] jtag/drivers: Add Cypress KitProg driver This patch adds a driver for the SWD-only Cypress KitProg programmer/debugger. Change-Id: I3a9a8011a762781d560ebb305597e782a4f9a8e5 Signed-off-by: Forest Crossman Reviewed-on: http://openocd.zylin.com/3221 Tested-by: jenkins Reviewed-by: Tomas Vanek --- configure.ac | 8 +- contrib/60-openocd.rules | 6 + doc/openocd.texi | 56 ++ src/jtag/drivers/Makefile.am | 4 + src/jtag/drivers/kitprog.c | 967 +++++++++++++++++++++++++++++++++++ src/jtag/interfaces.c | 6 + tcl/interface/kitprog.cfg | 12 + 7 files changed, 1058 insertions(+), 1 deletion(-) create mode 100644 src/jtag/drivers/kitprog.c create mode 100644 tcl/interface/kitprog.cfg diff --git a/configure.ac b/configure.ac index 6e60733ea..f5d3b9e7a 100644 --- a/configure.ac +++ b/configure.ac @@ -129,6 +129,9 @@ m4_define([USB0_ADAPTERS], m4_define([HIDAPI_ADAPTERS], [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP]]]) +m4_define([HIDAPI_USB1_ADAPTERS], + [[[kitprog], [Cypress KitProg Programmer], [KITPROG]]]) + m4_define([LIBFTDI_ADAPTERS], [[[usb_blaster], [Altera USB-Blaster Compatible], [USB_BLASTER]], [[presto], [ASIX Presto Adapter], [PRESTO]], @@ -243,6 +246,7 @@ AC_ARG_ADAPTERS([ USB_ADAPTERS, USB0_ADAPTERS, HIDAPI_ADAPTERS, + HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS, LIBJAYLINK_ADAPTERS ],[auto]) @@ -638,6 +642,7 @@ PROCESS_ADAPTERS([USB1_ADAPTERS], ["x$use_libusb1" = "xyes"], [libusb-1.x]) PROCESS_ADAPTERS([USB_ADAPTERS], ["x$use_libusb1" = "xyes" -o "x$use_libusb0" = "xyes"], [libusb-1.x or libusb-0.1]) PROCESS_ADAPTERS([USB0_ADAPTERS], ["x$use_libusb0" = "xyes"], [libusb-0.1]) PROCESS_ADAPTERS([HIDAPI_ADAPTERS], ["x$use_hidapi" = "xyes"], [hidapi]) +PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libusb1" = "xyes"], [hidapi and libusb-1.x]) PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_libusb1" = "xyes" -a "x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libusb-1.x or libjaylink-0.1]) @@ -768,7 +773,8 @@ echo echo OpenOCD configuration summary echo -------------------------------------------------- m4_foreach([adapter], [USB1_ADAPTERS, USB_ADAPTERS, USB0_ADAPTERS, - HIDAPI_ADAPTERS, LIBFTDI_ADAPTERS, LIBJAYLINK_ADAPTERS], + HIDAPI_ADAPTERS, HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS, + LIBJAYLINK_ADAPTERS], [s=m4_format(["%-40s"], ADAPTER_DESC([adapter])) AS_CASE([$ADAPTER_VAR([adapter])], [auto], [ diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules index 3597c95f6..da760f88a 100644 --- a/contrib/60-openocd.rules +++ b/contrib/60-openocd.rules @@ -62,6 +62,12 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="660", GROUP="plugdev", # STLink v2-1 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="660", GROUP="plugdev", TAG+="uaccess" +# Cypress KitProg in KitProg mode +ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="f139", MODE="660", GROUP="plugdev", TAG+="uaccess" + +# Cypress KitProg in CMSIS-DAP mode +ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="f138", MODE="660", GROUP="plugdev", TAG+="uaccess" + # Hilscher NXHX Boards ATTRS{idVendor}=="0640", ATTRS{idProduct}=="0028", MODE="660", GROUP="plugdev", TAG+="uaccess" diff --git a/doc/openocd.texi b/doc/openocd.texi index a60474ba1..845080efc 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2742,6 +2742,62 @@ As a configuration command, it can be used only before 'init'. @end deffn @end deffn +@deffn {Interface Driver} {kitprog} +This driver is for Cypress Semiconductor's KitProg adapters. The KitProg is an +SWD-only adapter that is designed to be used with Cypress's PSoC and PRoC device +families, but it is possible to use it with some other devices. If you are using +this adapter with a PSoC or a PRoC, you may need to add +@command{kitprog_init_acquire_psoc} or @command{kitprog acquire_psoc} to your +configuration script. + +Note that this driver is for the proprietary KitProg protocol, not the CMSIS-DAP +mode introduced in firmware 2.14. If the KitProg is in CMSIS-DAP mode, it cannot +be used with this driver, and must either be used with the cmsis-dap driver or +switched back to KitProg mode. See the Cypress KitProg User Guide for +instructions on how to switch KitProg modes. + +Known limitations: +@itemize @bullet +@item The frequency of SWCLK cannot be configured, and varies between 1.6 MHz +and 2.7 MHz. +@item For firmware versions below 2.14, "JTAG to SWD" sequences are replaced by +"SWD line reset" in the driver. This is for two reasons. First, the KitProg does +not support sending arbitrary SWD sequences, and only firmware 2.14 and later +implement both "JTAG to SWD" and "SWD line reset" in firmware. Earlier firmware +versions only implement "SWD line reset". Second, due to a firmware quirk, an +SWD sequence must be sent after every target reset in order to re-establish +communications with the target. +@item Due in part to the limitation above, KitProg devices with firmware below +version 2.14 will need to use @command{kitprog_init_acquire_psoc} in order to +communicate with PSoC 5LP devices. This is because, assuming debug is not +disabled on the PSoC, the PSoC 5LP needs its JTAG interface switched to SWD +mode before communication can begin, but prior to firmware 2.14, "JTAG to SWD" +could only be sent with an acquisition sequence. +@end itemize + +@deffn {Config Command} {kitprog_init_acquire_psoc} +Indicate that a PSoC acquisition sequence needs to be run during adapter init. +Please be aware that the acquisition sequence hard-resets the target. +@end deffn + +@deffn {Config Command} {kitprog_serial} serial +Select a KitProg device by its @var{serial}. If left unspecified, the first +device detected by OpenOCD will be used. +@end deffn + +@deffn {Command} {kitprog acquire_psoc} +Run a PSoC acquisition sequence immediately. Typically, this should not be used +outside of the target-specific configuration scripts since it hard-resets the +target as a side-effect. +This is necessary for "reset halt" on some PSoC 4 series devices. +@end deffn + +@deffn {Command} {kitprog info} +Display various adapter information, such as the hardware version, firmware +version, and target voltage. +@end deffn +@end deffn + @deffn {Interface Driver} {parport} Supports PC parallel port bit-banging cables: Wigglers, PLD download cable, and more. diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e41141262..e0c542168 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -153,6 +153,10 @@ if CMSIS_DAP DRIVERFILES += %D%/cmsis_dap_usb.c endif +if KITPROG +DRIVERFILES += %D%/kitprog.c +endif + DRIVERHEADERS = \ %D%/bitbang.h \ %D%/bitq.h \ diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c new file mode 100644 index 000000000..c689848c8 --- /dev/null +++ b/src/jtag/drivers/kitprog.c @@ -0,0 +1,967 @@ +/*************************************************************************** + * Copyright (C) 2007 by Juergen Stuber * + * based on Dominic Rath's and Benedikt Sauter's usbprog.c * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2011 by Jean-Christophe PLAGNIOL-VIILARD * + * plagnioj@jcrosoft.com * + * * + * Copyright (C) 2015 by Marc Schink * + * openocd-dev@marcschink.de * + * * + * Copyright (C) 2015 by Paul Fertser * + * fercerpav@gmail.com * + * * + * Copyright (C) 2015-2017 by Forest Crossman * + * cyrozap@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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include +#include +#include + +#include "libusb_common.h" + +#define VID 0x04b4 +#define PID 0xf139 + +#define BULK_EP_IN 1 +#define BULK_EP_OUT 2 + +#define CONTROL_TYPE_READ 0x01 +#define CONTROL_TYPE_WRITE 0x02 + +#define CONTROL_COMMAND_PROGRAM 0x07 + +#define CONTROL_MODE_POLL_PROGRAMMER_STATUS 0x01 +#define CONTROL_MODE_RESET_TARGET 0x04 +#define CONTROL_MODE_SET_PROGRAMMER_PROTOCOL 0x40 +#define CONTROL_MODE_SYNCHRONIZE_TRANSFER 0x41 +#define CONTROL_MODE_ACQUIRE_SWD_TARGET 0x42 +#define CONTROL_MODE_SEND_SWD_SEQUENCE 0x43 + +#define PROTOCOL_JTAG 0x00 +#define PROTOCOL_SWD 0x01 + +#define DEVICE_PSOC4 0x00 +#define DEVICE_PSOC3 0x01 +#define DEVICE_UNKNOWN 0x02 +#define DEVICE_PSOC5 0x03 + +#define ACQUIRE_MODE_RESET 0x00 +#define ACQUIRE_MODE_POWER_CYCLE 0x01 + +#define SEQUENCE_LINE_RESET 0x00 +#define SEQUENCE_JTAG_TO_SWD 0x01 + +#define PROGRAMMER_NOK_NACK 0x00 +#define PROGRAMMER_OK_ACK 0x01 + +#define HID_TYPE_WRITE 0x00 +#define HID_TYPE_READ 0x01 +#define HID_TYPE_START 0x02 + +#define HID_COMMAND_POWER 0x80 +#define HID_COMMAND_VERSION 0x81 +#define HID_COMMAND_RESET 0x82 +#define HID_COMMAND_CONFIGURE 0x8f +#define HID_COMMAND_BOOTLOADER 0xa0 + +/* 512 bytes seems to work reliably */ +#define SWD_MAX_BUFFER_LENGTH 512 + +struct kitprog { + hid_device *hid_handle; + struct jtag_libusb_device_handle *usb_handle; + uint16_t packet_size; + uint16_t packet_index; + uint8_t *packet_buffer; + char *serial; + uint8_t hardware_version; + uint8_t minor_version; + uint8_t major_version; + uint16_t millivolts; + + bool supports_jtag_to_swd; +}; + +struct pending_transfer_result { + uint8_t cmd; + uint32_t data; + void *buffer; +}; + +static char *kitprog_serial; +static bool kitprog_init_acquire_psoc; + +static int pending_transfer_count, pending_queue_len; +static struct pending_transfer_result *pending_transfers; + +static int queued_retval; + +static struct kitprog *kitprog_handle; + +static int kitprog_usb_open(void); +static void kitprog_usb_close(void); + +static int kitprog_hid_command(uint8_t *command, size_t command_length, + uint8_t *data, size_t data_length); +static int kitprog_get_version(void); +static int kitprog_get_millivolts(void); +static int kitprog_get_info(void); +static int kitprog_set_protocol(uint8_t protocol); +static int kitprog_get_status(void); +static int kitprog_set_unknown(void); +static int kitprog_acquire_psoc(uint8_t psoc_type, uint8_t acquire_mode, + uint8_t max_attempts); +static int kitprog_reset_target(void); +static int kitprog_swd_sync(void); +static int kitprog_swd_seq(uint8_t seq_type); + +static int kitprog_generic_acquire(void); + +static int kitprog_swd_run_queue(void); +static void kitprog_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data); +static int kitprog_swd_switch_seq(enum swd_special_seq seq); + + +static inline int mm_to_version(uint8_t major, uint8_t minor) +{ + return (major << 8) | minor; +} + +static int kitprog_init(void) +{ + int retval; + + kitprog_handle = malloc(sizeof(struct kitprog)); + if (kitprog_handle == NULL) { + LOG_ERROR("Failed to allocate memory"); + return ERROR_FAIL; + } + + if (kitprog_usb_open() != ERROR_OK) { + LOG_ERROR("Can't find a KitProg device! Please check device connections and permissions."); + return ERROR_JTAG_INIT_FAILED; + } + + /* Get the current KitProg version and target voltage */ + if (kitprog_get_info() != ERROR_OK) + return ERROR_FAIL; + + /* Compatibility check */ + kitprog_handle->supports_jtag_to_swd = true; + int kitprog_version = mm_to_version(kitprog_handle->major_version, kitprog_handle->minor_version); + if (kitprog_version < mm_to_version(2, 14)) { + LOG_WARNING("KitProg firmware versions below v2.14 do not support sending JTAG to SWD sequences. These sequences will be substituted with SWD line resets."); + kitprog_handle->supports_jtag_to_swd = false; + } + + /* I have no idea what this does */ + if (kitprog_set_unknown() != ERROR_OK) + return ERROR_FAIL; + + /* SWD won't work unless we do this */ + if (kitprog_swd_sync() != ERROR_OK) + return ERROR_FAIL; + + /* Set the protocol to SWD */ + if (kitprog_set_protocol(PROTOCOL_SWD) != ERROR_OK) + return ERROR_FAIL; + + /* Reset the SWD bus */ + if (kitprog_swd_seq(SEQUENCE_LINE_RESET) != ERROR_OK) + return ERROR_FAIL; + + if (kitprog_init_acquire_psoc) { + /* Try to acquire any device that will respond */ + retval = kitprog_generic_acquire(); + if (retval != ERROR_OK) { + LOG_ERROR("No PSoC devices found"); + return retval; + } + } + + /* Allocate packet buffers and queues */ + kitprog_handle->packet_size = SWD_MAX_BUFFER_LENGTH; + kitprog_handle->packet_buffer = malloc(SWD_MAX_BUFFER_LENGTH); + if (kitprog_handle->packet_buffer == NULL) { + LOG_ERROR("Failed to allocate memory for the packet buffer"); + return ERROR_FAIL; + } + + pending_queue_len = SWD_MAX_BUFFER_LENGTH / 5; + pending_transfers = malloc(pending_queue_len * sizeof(*pending_transfers)); + if (pending_transfers == NULL) { + LOG_ERROR("Failed to allocate memory for the SWD transfer queue"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_quit(void) +{ + kitprog_usb_close(); + + if (kitprog_handle->packet_buffer != NULL) + free(kitprog_handle->packet_buffer); + if (kitprog_handle->serial != NULL) + free(kitprog_handle->serial); + if (kitprog_handle != NULL) + free(kitprog_handle); + + if (kitprog_serial != NULL) + free(kitprog_serial); + + if (pending_transfers != NULL) + free(pending_transfers); + + return ERROR_OK; +} + +/*************** kitprog usb functions *********************/ + +static int kitprog_get_usb_serial(void) +{ + int retval; + const uint8_t str_index = 128; /* This seems to be a constant */ + char desc_string[256+1]; /* Max size of string descriptor */ + + retval = libusb_get_string_descriptor_ascii(kitprog_handle->usb_handle, + str_index, (unsigned char *)desc_string, sizeof(desc_string)-1); + if (retval < 0) { + LOG_ERROR("libusb_get_string_descriptor_ascii() failed with %d", retval); + return ERROR_FAIL; + } + + /* Null terminate descriptor string */ + desc_string[retval] = '\0'; + + /* Allocate memory for the serial number */ + kitprog_handle->serial = calloc(retval + 1, sizeof(char)); + if (kitprog_handle->serial == NULL) { + LOG_ERROR("Failed to allocate memory for the serial number"); + return ERROR_FAIL; + } + + /* Store the serial number */ + strncpy(kitprog_handle->serial, desc_string, retval + 1); + + return ERROR_OK; +} + +static int kitprog_usb_open(void) +{ + const uint16_t vids[] = { VID, 0 }; + const uint16_t pids[] = { PID, 0 }; + + if (jtag_libusb_open(vids, pids, kitprog_serial, + &kitprog_handle->usb_handle) != ERROR_OK) { + LOG_ERROR("Failed to open or find the device"); + return ERROR_FAIL; + } + + /* Get the serial number for the device */ + if (kitprog_get_usb_serial() != ERROR_OK) + LOG_WARNING("Failed to get KitProg serial number"); + + /* Convert the ASCII serial number into a (wchar_t *) */ + size_t len = strlen(kitprog_handle->serial); + wchar_t *hid_serial = calloc(len + 1, sizeof(wchar_t)); + if (hid_serial == NULL) { + LOG_ERROR("Failed to allocate memory for the serial number"); + return ERROR_FAIL; + } + if (mbstowcs(hid_serial, kitprog_handle->serial, len + 1) == (size_t)-1) { + free(hid_serial); + LOG_ERROR("Failed to convert serial number"); + return ERROR_FAIL; + } + + /* Use HID for the KitBridge interface */ + kitprog_handle->hid_handle = hid_open(VID, PID, hid_serial); + free(hid_serial); + if (kitprog_handle->hid_handle == NULL) { + LOG_ERROR("Failed to open KitBridge (HID) interface"); + return ERROR_FAIL; + } + + /* Claim the KitProg Programmer (bulk transfer) interface */ + if (jtag_libusb_claim_interface(kitprog_handle->usb_handle, 1) != ERROR_OK) { + LOG_ERROR("Failed to claim KitProg Programmer (bulk transfer) interface"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static void kitprog_usb_close(void) +{ + if (kitprog_handle->hid_handle != NULL) { + hid_close(kitprog_handle->hid_handle); + hid_exit(); + } + + jtag_libusb_close(kitprog_handle->usb_handle); +} + +/*************** kitprog lowlevel functions *********************/ + +static int kitprog_hid_command(uint8_t *command, size_t command_length, + uint8_t *data, size_t data_length) +{ + int ret; + + ret = hid_write(kitprog_handle->hid_handle, command, command_length); + if (ret < 0) { + LOG_DEBUG("HID write returned %i", ret); + return ERROR_FAIL; + } + + ret = hid_read(kitprog_handle->hid_handle, data, data_length); + if (ret < 0) { + LOG_DEBUG("HID read returned %i", ret); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_get_version(void) +{ + int ret; + + unsigned char command[3] = {HID_TYPE_START | HID_TYPE_WRITE, 0x00, HID_COMMAND_VERSION}; + unsigned char data[64]; + + ret = kitprog_hid_command(command, sizeof command, data, sizeof data); + if (ret != ERROR_OK) + return ret; + + kitprog_handle->hardware_version = data[1]; + kitprog_handle->minor_version = data[2]; + kitprog_handle->major_version = data[3]; + + return ERROR_OK; +} + +static int kitprog_get_millivolts(void) +{ + int ret; + + unsigned char command[3] = {HID_TYPE_START | HID_TYPE_READ, 0x00, HID_COMMAND_POWER}; + unsigned char data[64]; + + ret = kitprog_hid_command(command, sizeof command, data, sizeof data); + if (ret != ERROR_OK) + return ret; + + kitprog_handle->millivolts = (data[4] << 8) | data[3]; + + return ERROR_OK; +} + +static int kitprog_get_info(void) +{ + /* Get the device version information */ + if (kitprog_get_version() == ERROR_OK) { + LOG_INFO("KitProg v%u.%02u", + kitprog_handle->major_version, kitprog_handle->minor_version); + LOG_INFO("Hardware version: %u", + kitprog_handle->hardware_version); + } else { + LOG_ERROR("Failed to get KitProg version"); + return ERROR_FAIL; + } + + /* Get the current reported target voltage */ + if (kitprog_get_millivolts() == ERROR_OK) { + LOG_INFO("VTARG = %u.%03u V", + kitprog_handle->millivolts / 1000, kitprog_handle->millivolts % 1000); + } else { + LOG_ERROR("Failed to get target voltage"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_set_protocol(uint8_t protocol) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (CONTROL_MODE_SET_PROGRAMMER_PROTOCOL << 8) | CONTROL_COMMAND_PROGRAM, + protocol, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_get_status(void) +{ + int transferred = 0; + char status = PROGRAMMER_NOK_NACK; + + /* Try a maximum of three times */ + for (int i = 0; (i < 3) && (transferred == 0); i++) { + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_READ, + (CONTROL_MODE_POLL_PROGRAMMER_STATUS << 8) | CONTROL_COMMAND_PROGRAM, + 0, &status, 1, 0); + jtag_sleep(1000); + } + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_set_unknown(void) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (0x03 << 8) | 0x04, + 0, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_acquire_psoc(uint8_t psoc_type, uint8_t acquire_mode, + uint8_t max_attempts) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (CONTROL_MODE_ACQUIRE_SWD_TARGET << 8) | CONTROL_COMMAND_PROGRAM, + (max_attempts << 8) | (acquire_mode << 4) | psoc_type, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_reset_target(void) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (CONTROL_MODE_RESET_TARGET << 8) | CONTROL_COMMAND_PROGRAM, + 0, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_swd_sync(void) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (CONTROL_MODE_SYNCHRONIZE_TRANSFER << 8) | CONTROL_COMMAND_PROGRAM, + 0, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_swd_seq(uint8_t seq_type) +{ + int transferred; + char status = PROGRAMMER_NOK_NACK; + + transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + CONTROL_TYPE_WRITE, + (CONTROL_MODE_SEND_SWD_SEQUENCE << 8) | CONTROL_COMMAND_PROGRAM, + seq_type, &status, 1, 0); + + if (transferred == 0) { + LOG_DEBUG("Zero bytes transferred"); + return ERROR_FAIL; + } + + if (status != PROGRAMMER_OK_ACK) { + LOG_DEBUG("Programmer did not respond OK"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_generic_acquire(void) +{ + const uint8_t devices[] = {DEVICE_PSOC4, DEVICE_PSOC3, DEVICE_PSOC5}; + + int retval; + int acquire_count = 0; + + /* Due to the way the SWD port is shared between the Test Controller (TC) + * and the Cortex-M3 DAP on the PSoC 5LP, the TC is the default SWD target + * after power is applied. To access the DAP, the PSoC 5LP requires at least + * one acquisition sequence to be run (which switches the SWD mux from the + * TC to the DAP). However, after the mux is switched, the Cortex-M3 will be + * held in reset until a series of registers are written to (see section 5.2 + * of the PSoC 5LP Device Programming Specifications for details). + * + * Instead of writing the registers in this function, we just do what the + * Cypress tools do and run the acquisition sequence a second time. This + * will take the Cortex-M3 out of reset and enable debugging. + */ + for (int i = 0; i < 2; i++) { + for (uint8_t j = 0; j < sizeof devices && acquire_count == i; j++) { + retval = kitprog_acquire_psoc(devices[j], ACQUIRE_MODE_RESET, 3); + if (retval != ERROR_OK) { + LOG_DEBUG("Aquisition function failed for device 0x%02x.", devices[j]); + return retval; + } + + if (kitprog_get_status() == ERROR_OK) + acquire_count++; + } + + jtag_sleep(10); + } + + if (acquire_count < 2) + return ERROR_FAIL; + + return ERROR_OK; +} + +/*************** swd wrapper functions *********************/ + +static int kitprog_swd_init(void) +{ + return ERROR_OK; +} + +static void kitprog_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) +{ + assert(!(cmd & SWD_CMD_RnW)); + kitprog_swd_queue_cmd(cmd, NULL, value); +} + +static void kitprog_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) +{ + assert(cmd & SWD_CMD_RnW); + kitprog_swd_queue_cmd(cmd, value, 0); +} + +/*************** swd lowlevel functions ********************/ + +static int kitprog_swd_switch_seq(enum swd_special_seq seq) +{ + switch (seq) { + case JTAG_TO_SWD: + if (kitprog_handle->supports_jtag_to_swd) { + LOG_DEBUG("JTAG to SWD"); + if (kitprog_swd_seq(SEQUENCE_JTAG_TO_SWD) != ERROR_OK) + return ERROR_FAIL; + break; + } else { + LOG_DEBUG("JTAG to SWD not supported"); + /* Fall through to fix target reset issue */ + } + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + if (kitprog_swd_seq(SEQUENCE_LINE_RESET) != ERROR_OK) + return ERROR_FAIL; + break; + default: + LOG_ERROR("Sequence %d not supported.", seq); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kitprog_swd_run_queue(void) +{ + int ret; + + size_t read_count = 0; + size_t read_index = 0; + size_t write_count = 0; + uint8_t *buffer = kitprog_handle->packet_buffer; + + do { + LOG_DEBUG("Executing %d queued transactions", pending_transfer_count); + + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skipping due to previous errors: %d", queued_retval); + break; + } + + if (!pending_transfer_count) + break; + + for (int i = 0; i < pending_transfer_count; i++) { + uint8_t cmd = pending_transfers[i].cmd; + uint32_t data = pending_transfers[i].data; + + /* When proper WAIT handling is implemented in the + * common SWD framework, this kludge can be + * removed. However, this might lead to minor + * performance degradation as the adapter wouldn't be + * able to automatically retry anything (because ARM + * has forgotten to implement sticky error flags + * clearing). See also comments regarding + * cmsis_dap_cmd_DAP_TFER_Configure() and + * cmsis_dap_cmd_DAP_SWD_Configure() in + * cmsis_dap_init(). + */ + if (!(cmd & SWD_CMD_RnW) && + !(cmd & SWD_CMD_APnDP) && + (cmd & SWD_CMD_A32) >> 1 == DP_CTRL_STAT && + (data & CORUNDETECT)) { + LOG_DEBUG("refusing to enable sticky overrun detection"); + data &= ~CORUNDETECT; + } + +#if 0 + LOG_DEBUG("%s %s reg %x %"PRIx32, + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, data); +#endif + + buffer[write_count++] = (cmd | SWD_CMD_START | SWD_CMD_PARK) & ~SWD_CMD_STOP; + read_count++; + if (!(cmd & SWD_CMD_RnW)) { + buffer[write_count++] = (data) & 0xff; + buffer[write_count++] = (data >> 8) & 0xff; + buffer[write_count++] = (data >> 16) & 0xff; + buffer[write_count++] = (data >> 24) & 0xff; + } else { + read_count += 4; + } + } + + ret = jtag_libusb_bulk_write(kitprog_handle->usb_handle, + BULK_EP_OUT, (char *)buffer, write_count, 0); + if (ret > 0) { + queued_retval = ERROR_OK; + } else { + LOG_ERROR("Bulk write failed"); + queued_retval = ERROR_FAIL; + break; + } + + /* We use the maximum buffer size here because the KitProg sometimes + * doesn't like bulk reads of fewer than 62 bytes. (?!?!) + */ + ret = jtag_libusb_bulk_read(kitprog_handle->usb_handle, + BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)buffer, + SWD_MAX_BUFFER_LENGTH, 0); + if (ret > 0) { + /* Handle garbage data by offsetting the initial read index */ + if ((unsigned int)ret > read_count) + read_index = ret - read_count; + queued_retval = ERROR_OK; + } else { + LOG_ERROR("Bulk read failed"); + queued_retval = ERROR_FAIL; + break; + } + + for (int i = 0; i < pending_transfer_count; i++) { + if (pending_transfers[i].cmd & SWD_CMD_RnW) { + uint32_t data = le_to_h_u32(&buffer[read_index]); + +#if 0 + LOG_DEBUG("Read result: %"PRIx32, data); +#endif + + if (pending_transfers[i].buffer) + *(uint32_t *)pending_transfers[i].buffer = data; + + read_index += 4; + } + + uint8_t ack = buffer[read_index] & 0x07; + if (ack != SWD_ACK_OK || (buffer[read_index] & 0x08)) { + LOG_DEBUG("SWD ack not OK: %d %s", i, + ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK"); + queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL; + break; + } + read_index++; + } + } while (0); + + pending_transfer_count = 0; + int retval = queued_retval; + queued_retval = ERROR_OK; + + return retval; +} + +static void kitprog_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) +{ + if (pending_transfer_count == pending_queue_len) { + /* Not enough room in the queue. Run the queue. */ + queued_retval = kitprog_swd_run_queue(); + } + + if (queued_retval != ERROR_OK) + return; + + pending_transfers[pending_transfer_count].data = data; + pending_transfers[pending_transfer_count].cmd = cmd; + if (cmd & SWD_CMD_RnW) { + /* Queue a read transaction */ + pending_transfers[pending_transfer_count].buffer = dst; + } + pending_transfer_count++; +} + +/*************** jtag lowlevel functions ********************/ + +static void kitprog_execute_reset(struct jtag_command *cmd) +{ + int retval = ERROR_OK; + + if (cmd->cmd.reset->srst == 1) { + retval = kitprog_reset_target(); + /* Since the previous command also disables SWCLK output, we need to send an + * SWD bus reset command to re-enable it. For some reason, running + * kitprog_swd_seq() immediately after kitprog_reset_target() won't + * actually fix this. Instead, kitprog_swd_seq() will be run once OpenOCD + * tries to send a JTAG-to-SWD sequence, which should happen during + * swd_check_reconnect (see the JTAG_TO_SWD case in kitprog_swd_switch_seq). + */ + } + + if (retval != ERROR_OK) + LOG_ERROR("KitProg: Interface reset failed"); +} + +static void kitprog_execute_sleep(struct jtag_command *cmd) +{ + jtag_sleep(cmd->cmd.sleep->us); +} + +static void kitprog_execute_command(struct jtag_command *cmd) +{ + switch (cmd->type) { + case JTAG_RESET: + kitprog_execute_reset(cmd); + break; + case JTAG_SLEEP: + kitprog_execute_sleep(cmd); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } +} + +static int kitprog_execute_queue(void) +{ + struct jtag_command *cmd = jtag_command_queue; + + while (cmd != NULL) { + kitprog_execute_command(cmd); + cmd = cmd->next; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(kitprog_handle_info_command) +{ + int retval = kitprog_get_info(); + + return retval; +} + + +COMMAND_HANDLER(kitprog_handle_acquire_psoc_command) +{ + int retval = kitprog_generic_acquire(); + + return retval; +} + +COMMAND_HANDLER(kitprog_handle_serial_command) +{ + if (CMD_ARGC == 1) { + size_t len = strlen(CMD_ARGV[0]); + kitprog_serial = calloc(len + 1, sizeof(char)); + if (kitprog_serial == NULL) { + LOG_ERROR("Failed to allocate memory for the serial number"); + return ERROR_FAIL; + } + strncpy(kitprog_serial, CMD_ARGV[0], len + 1); + } else { + LOG_ERROR("expected exactly one argument to kitprog_serial "); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(kitprog_handle_init_acquire_psoc_command) +{ + kitprog_init_acquire_psoc = true; + + return ERROR_OK; +} + +static const struct command_registration kitprog_subcommand_handlers[] = { + { + .name = "info", + .handler = &kitprog_handle_info_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "show KitProg info", + }, + { + .name = "acquire_psoc", + .handler = &kitprog_handle_acquire_psoc_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "try to acquire a PSoC", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration kitprog_command_handlers[] = { + { + .name = "kitprog", + .mode = COMMAND_ANY, + .help = "perform KitProg management", + .usage = "", + .chain = kitprog_subcommand_handlers, + }, + { + .name = "kitprog_serial", + .handler = &kitprog_handle_serial_command, + .mode = COMMAND_CONFIG, + .help = "set the serial number of the adapter", + .usage = "serial_string", + }, + { + .name = "kitprog_init_acquire_psoc", + .handler = &kitprog_handle_init_acquire_psoc_command, + .mode = COMMAND_CONFIG, + .help = "try to acquire a PSoC during init", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct swd_driver kitprog_swd = { + .init = kitprog_swd_init, + .switch_seq = kitprog_swd_switch_seq, + .read_reg = kitprog_swd_read_reg, + .write_reg = kitprog_swd_write_reg, + .run = kitprog_swd_run_queue, +}; + +static const char * const kitprog_transports[] = { "swd", NULL }; + +struct jtag_interface kitprog_interface = { + .name = "kitprog", + .commands = kitprog_command_handlers, + .transports = kitprog_transports, + .swd = &kitprog_swd, + .execute_queue = kitprog_execute_queue, + .init = kitprog_init, + .quit = kitprog_quit +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index ad656a84b..396043d26 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -123,6 +123,9 @@ extern struct jtag_interface bcm2835gpio_interface; #if BUILD_CMSIS_DAP == 1 extern struct jtag_interface cmsis_dap_interface; #endif +#if BUILD_KITPROG == 1 +extern struct jtag_interface kitprog_interface; +#endif #endif /* standard drivers */ /** @@ -216,6 +219,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_CMSIS_DAP == 1 &cmsis_dap_interface, #endif +#if BUILD_KITPROG == 1 + &kitprog_interface, +#endif #endif /* standard drivers */ NULL, }; diff --git a/tcl/interface/kitprog.cfg b/tcl/interface/kitprog.cfg new file mode 100644 index 000000000..94497147f --- /dev/null +++ b/tcl/interface/kitprog.cfg @@ -0,0 +1,12 @@ +# +# Cypress Semiconductor KitProg +# +# Note: This is the driver for the proprietary KitPtog protocol. If the +# KitProg is in CMSIS-DAP mode, you should either use the cmsis-dap +# interface driver or switch the KitProg to KitProg mode. +# + +interface kitprog + +# Optionally specify the serial number of the KitProg you want to use. +#kitprog_serial 1926402735485200