From f6449a7cba11de589c40169a7dd3b183bd60d1f4 Mon Sep 17 00:00:00 2001
From: Forest Crossman <cyrozap@gmail.com>
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 <cyrozap@gmail.com>
Reviewed-on: http://openocd.zylin.com/3221
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
---
 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 <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
+};
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
-- 
GitLab