jtag/drivers: Add Cypress KitProg driver 21/3221/48
authorForest Crossman <cyrozap@gmail.com>
Sat, 30 Jan 2016 05:23:49 +0000 (00:23 -0500)
committerPaul Fertser <fercerpav@gmail.com>
Fri, 12 May 2017 09:08:43 +0000 (10:08 +0100)
This patch adds a driver for the SWD-only Cypress KitProg
programmer/debugger.

Change-Id: I3a9a8011a762781d560ebb305597e782a4f9a8e5
Signed-off-by: Forest Crossman <cyrozap@gmail.com>
Reviewed-on: http://openocd.zylin.com/3221
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
configure.ac
contrib/60-openocd.rules
doc/openocd.texi
src/jtag/drivers/Makefile.am
src/jtag/drivers/kitprog.c [new file with mode: 0644]
src/jtag/interfaces.c
tcl/interface/kitprog.cfg [new file with mode: 0644]

index 6e60733eaf358dd56009068c0786c0192b421f34..f5d3b9e7ad1aea1cce1e395bc4d62eb7a57ae353 100644 (file)
@@ -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], [
index 3597c95f6c039785d3391beef6aee2cf5ab3fff4..da760f88ad500774d7aa863150af3a9f643497ad 100644 (file)
@@ -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"
 
index a60474ba1bdadab54c6dcd18ae65e18976d41dd8..845080efc9ce89b9681df7da963a9fa17d842fff 100644 (file)
@@ -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.
index e41141262623cbdc8f63eb2a1d53d82e0068ec4d..e0c5421689afefe78cb0ef1e08bee0124e8c0c16 100644 (file)
@@ -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 (file)
index 0000000..c689848
--- /dev/null
@@ -0,0 +1,967 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by Juergen Stuber <juergen@jstuber.net>            *
+ *   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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+
+#include <hidapi.h>
+
+#include <jtag/interface.h>
+#include <jtag/swd.h>
+#include <jtag/commands.h>
+
+#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 <serial-number>");
+               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 = "<cmd>",
+               .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
+};
index ad656a84b1a6ee716298d203a389a8557023b434..396043d268da1766a93d234a9632df0d4507bf65 100644 (file)
@@ -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 (file)
index 0000000..9449714
--- /dev/null
@@ -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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)