This is an automated email from Gerrit.

"Conor Paxton <conor.pax...@microchip.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8371

-- gerrit

commit 0dd0876a30f1853b6e06aa26d58d1556f883457d
Author: Conor Paxton <conor.pax...@microchip.com>
Date:   Wed May 15 18:49:44 2024 +0100

    jtag: drivers: add microchip flashpro6 support
    
    Microchip's FlashPro6 programmer provides support for Polarfire and
    PolarFire SoC. This patch adds driver source code.
    
    Change-Id: I54772c4315b081ab1afa0fdceb688f2243829f0e
    Signed-off-by: Tommy Murphy <tommy_mur...@hotmail.com>
    Signed-off-by: Anton Krug <anton.k...@gmail.com>
    Signed-off-by: Daire McNamara <daire.mcnam...@microchip.com>
    Signed-off-by: Matteo Bonicolini <matteo.bonicol...@microchip.com>
    Signed-off-by: Conor Paxton <conor.pax...@microchip.com>

diff --git a/configure.ac b/configure.ac
index 9355ffb932..da1ad35c7c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -347,6 +347,10 @@ AC_ARG_ENABLE([sysfsgpio],
   AS_HELP_STRING([--enable-sysfsgpio], [Enable building support for 
programming driven via sysfs gpios.]),
   [build_sysfsgpio=$enableval], [build_sysfsgpio=no])
 
+AC_ARG_ENABLE([microchip_fp6],
+  AS_HELP_STRING([--enable-microchip-fp6], [Enable building support for 
Microchip FlashPro6 Programmer]),
+  [build_microchip_fp6=$enableval], [build_microchip_fp6=no])
+
 AS_CASE([$host_os],
   [linux*], [
     is_linux=yes
@@ -620,6 +624,13 @@ AS_IF([test "x$build_sysfsgpio" = "xyes"], [
   AC_DEFINE([BUILD_SYSFSGPIO], [0], [0 if you don't want SysfsGPIO driver.])
 ])
 
+AS_IF([test "x$build_microchip_fp6" = "xyes"], [
+  build_microchip_fp6=yes
+  AC_DEFINE([BUILD_MICROCHIP_FP6], [1], [1 if you want the Microchip FlashPro6 
driver.])
+], [
+  AC_DEFINE([BUILD_MICROCHIP_FP6], [0], [0 if you don't the Microchip 
FlashPro6 driver.])
+])
+
 PKG_CHECK_MODULES([LIBUSB1], [libusb-1.0], [
        use_libusb1=yes
        AC_DEFINE([HAVE_LIBUSB1], [1], [Define if you have libusb-1.x])
@@ -782,6 +793,7 @@ AM_CONDITIONAL([USE_LIBJAYLINK], [test "x$use_libjaylink" = 
"xyes"])
 AM_CONDITIONAL([RSHIM], [test "x$build_rshim" = "xyes"])
 AM_CONDITIONAL([DMEM], [test "x$build_dmem" = "xyes"])
 AM_CONDITIONAL([HAVE_CAPSTONE], [test "x$enable_capstone" != "xno"])
+AM_CONDITIONAL([MICROCHIP_FP6], [test "x$build_microchip_fp6" = "xyes"])
 
 AM_CONDITIONAL([INTERNAL_JIMTCL], [test "x$use_internal_jimtcl" = "xyes"])
 AM_CONDITIONAL([HAVE_JIMTCL_PKG_CONFIG], [test "x$have_jimtcl_pkg_config" = 
"xyes"])
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index e404afe9f0..fec05e5f2c 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -204,6 +204,9 @@ endif
 if AM335XGPIO
 DRIVERFILES += %D%/am335xgpio.c
 endif
+if MICROCHIP_FP6
+DRIVERFILES += %D%/mchp_fp6.c
+endif
 
 DRIVERHEADERS = \
        %D%/bitbang.h \
@@ -221,4 +224,6 @@ DRIVERHEADERS = \
        %D%/versaloon/usbtoxxx/usbtoxxx_internal.h \
        %D%/versaloon/versaloon.h \
        %D%/versaloon/versaloon_include.h \
-       %D%/versaloon/versaloon_internal.h
+       %D%/versaloon/versaloon_internal.h \
+       %D%/mchp_fp.h \
+       %D%/mchp_fp6.h
diff --git a/src/jtag/drivers/mchp_fp.h b/src/jtag/drivers/mchp_fp.h
new file mode 100644
index 0000000000..e678d6c530
--- /dev/null
+++ b/src/jtag/drivers/mchp_fp.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef MCHP_FP
+#define MCHP_FP
+
+#define HZ_PER_KHZ                                     1000
+
+#define FP_MAX_USB_BUFFER_BYTE_SIZE            1024
+
+#define FP_READ_TIMEOUT                                        3500
+
+#define FP_PACKET_START_CODE                           0x01
+
+/* sub commands */
+#define FP_SET_TCK_FREQUENCY                           0
+#define FP_FREQUENCY_READ_BYTE_LOCATION                        13
+
+#define FP_TARGET_FREQUENCY_ADDRESS                    0xa0fffff0
+
+/* Opcodes */
+#define SEND_PACKET_OPCODE                             0x11
+#define DDR_REMAP_OPCODE                               0x12
+#define ENVM_REMAP_OPCODE                              0x13
+#define READ_PRINT_BUFFER_OPCODE                       0x21
+#define M3_IN_RESET_OPCODE                             0x23
+#define M3_OUT_OF_RESET_OPCODE                         0x24
+#define FP6_READ_TCK_OPCODE                            0x25
+#define READ_PROGRESS_OPCODE                           0x26
+#define MSS_IN_RESET_OPCODE                            0x27
+#define MSS_OUT_OF_RESET_OPCODE                                0x28
+#define INTERRUPT_OPCODE                               0x29
+#define JTAG_ATOMIC_OPCODE                             0x2a
+#define SET_SHIFT_IR_DR_BIT_LENGTH_OPCODE              0x2c
+#define SHIFT_DATA_FROM_REGISTER_OPCODE                        0x2d
+#define SHIFT_DATA_FROM_DDR_OPCODE                     0x2e
+#define READ_TDO_BUFFER_COMMAND_OPCODE                 0x2f
+#define FREQUENCY_INTERRUPT_OPCODE                     0x30
+#define INSERT_WAIT_CYCLES_OPCODE                      0x31
+#define ENABLE_JTAG_PORT_OPCODE                                0x32
+#define DISABLE_JTAG_PORT_OPCODE                       0x33
+#define SET_JTAG_PINS_OPCODE                           0x34
+#define GET_JTAG_PINS_OPCODE                           0x35
+#define GET_CM3_STATUS_OPCODE                          0x36
+#define SET_CHAIN_PARAMETER_OPCODE                     0x37
+#define READ_CHAIN_PARAMETER_OPCODE                    0x38
+#define READ_INBOX_OPCODE                              0x39
+#define TURN_ACTIVITY_LED_ON_OPCODE                    0x41
+#define TURN_ACTIVITY_LED_OFF_OPCODE                   0x42
+/* eFP6 Vendor Commands */
+#define EFP6_CM3_FW_VERSION_OPCODE                             0xc0
+/* FP6 Vendor Commands */
+#define FP6_PROGRAM_I2C_VENDOR_OPCODE                  0xba
+#define FP6_SET_PROGRAMMER_SERIAL_NUMBER_VENDOR_OPCODE 0xba
+#define FP6_GET_PROGRAMMER_SERIAL_NUMBER_VENDOR_OPCODE 0xbb
+#define FP6_GET_FX3_FIRMWARE_VERSION_VENDOR_OPCODE     0xc0
+#define FP6_SET_LED_OFF_VENDOR_OPCODE                  0xc1
+#define FP6_SET_LED_PASS_VENDOR_OPCODE                 0xc2
+#define FP6_SET_LED_FAIL_VENDOR_OPCODE                 0xc3
+#define FP6_SET_VPUMP_LOW_VENDOR_OPCODE                        0xc4
+#define FP6_SET_VPUMP_HIGH_VENDOR_OPCODE               0xc5
+#define FP6_SET_PROG_MODE_LOW_VENDOR_OPCODE            0xc6
+#define FP6_SET_PROG_MODE_HIGH_VENDOR_OPCODE           0xc7
+#define FP6_FX3_RESET_OPCODE                           0xc8
+#define FP6_SF2_RESET_OPCODE                           0xc9
+#define FP6_READ_ADC_VPUMP_VENDOR_OPCODE               0xee
+#define FP6_READ_ADC_VJTAG_VENDOR_OPCODE               0xef
+
+#define COMMAND_PACKET_LENGTH                          14
+#define ACTION_PACKET_LENGTH                           16
+#define READ_PACKET_LENGTH                             128
+#define PROGRESS_PACKET_LENGTH                         128
+#define PAYLOAD_ATOMIC_PACKET_LENGTH                   16
+#define ATOMIC_JTAG_OPERATION_PACKET_LENGTH            2
+
+#endif
diff --git a/src/jtag/drivers/mchp_fp6.c b/src/jtag/drivers/mchp_fp6.c
new file mode 100644
index 0000000000..3db627ee6e
--- /dev/null
+++ b/src/jtag/drivers/mchp_fp6.c
@@ -0,0 +1,1203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Copyright (C) 2024 by Microchip Inc.                                  *
+ *   matteo.bonicol...@microchip.com                                       *
+ ***************************************************************************/
+
+#include <jtag/interface.h>
+#include <libusb.h>
+#include "libusb_helper.h"
+
+#include "helper/log.h"
+#include "helper/time_support.h"
+#include "mchp_fp6.h"
+
+/* Compatibility define for older libusb-1.0 */
+#ifndef LIBUSB_CALL
+#define LIBUSB_CALL
+#endif
+
+struct fp6_ctx {
+       struct libusb_context *usb_ctx;
+       struct libusb_device_handle *usb_dev;
+       uint32_t usb_write_timeout;
+       uint32_t usb_read_timeout;
+       uint8_t in_ep;
+       uint8_t out_ep;
+       uint16_t max_packet_size;
+       uint16_t index;
+       uint16_t value;
+       uint8_t interface;
+       uint8_t *write_buffer;
+       uint32_t write_size;
+       uint32_t write_count;
+       uint8_t *read_buffer;
+       uint32_t read_size;
+       uint32_t read_count;
+       uint8_t *read_chunk;
+       uint32_t read_chunk_size;
+       struct bit_copy_queue read_queue;
+       int retval;
+};
+
+static struct fp6_ctx *fp6_ctx;
+static char serial_num[126];
+static bool select_serial_num;
+
+static int mchp_fp6_quit(void)
+{
+       int err;
+
+       err = libusb_release_interface(fp6_ctx->usb_dev, fp6_ctx->interface);
+       if (err)
+               return err;
+
+       libusb_close(fp6_ctx->usb_dev);
+       libusb_exit(fp6_ctx->usb_ctx);
+
+       return ERROR_OK;
+}
+
+/* Returns true if the string descriptor indexed by str_index in device 
matches string */
+static bool string_descriptor_equal(struct libusb_device_handle *device, 
uint8_t str_index,
+       const char *string)
+{
+       int retval;
+       char desc_string[256]; /* Max size of string descriptor */
+
+       retval = libusb_get_string_descriptor_ascii(device, str_index, (uint8_t 
*)desc_string,
+                       sizeof(desc_string));
+       if (retval < 0) {
+               LOG_ERROR("libusb_get_string_descriptor_ascii() failed with 
%s", libusb_error_name(retval));
+               return false;
+       }
+
+       return strncmp(string, desc_string, sizeof(desc_string)) == 0;
+}
+
+static bool device_location_equal(struct libusb_device *device, const char 
*location)
+{
+       bool result = false;
+#ifdef HAVE_LIBUSB_GET_PORT_NUMBERS
+       char *loc = strdup(location);
+       uint8_t port_path[7];
+       int path_step, path_len;
+       uint8_t dev_bus = libusb_get_bus_number(device);
+       char const *ptr;
+
+       path_len = libusb_get_port_numbers(device, port_path, 7);
+       if (path_len == LIBUSB_ERROR_OVERFLOW) {
+               LOG_ERROR("cannot determine path to usb device! (more than 7 
ports in path)");
+               goto done;
+       }
+
+       ptr = strtok(loc, "-:");
+       if (!ptr) {
+               LOG_ERROR("no ':' in path");
+               goto done;
+       }
+       if (atoi(ptr) != dev_bus) {
+               LOG_ERROR("bus mismatch");
+               goto done;
+       }
+
+       path_step = 0;
+       while (path_step < 7) {
+               ptr = strtok(NULL, ".,");
+               if (!ptr) {
+                       LOG_ERROR("no more tokens in path at step %i", 
path_step);
+                       break;
+               }
+
+               if (path_step < path_len && atoi(ptr) != port_path[path_step]) {
+                       LOG_ERROR("path mismatch at step %i", path_step);
+                       break;
+               }
+
+               path_step++;
+       }
+
+       /* walked the full path, all elements match */
+       if (path_step == path_len)
+               result = true;
+
+done:
+       free(loc);
+#endif
+       return result;
+}
+
+static void add_usb_packet_header(struct fp6_ctx *ctx, uint16_t start_code, 
uint16_t packet_type,
+                                                       uint32_t 
target_address, uint32_t packet_length, uint16_t packet_crc)
+{
+       int offset = ctx->write_count;
+
+       ctx->write_buffer[offset + 0] = start_code;
+       ctx->write_buffer[offset + 1] = start_code >> 8;
+
+       ctx->write_buffer[offset + 2] = packet_type;
+       ctx->write_buffer[offset + 3] = packet_type >> 8;
+
+       ctx->write_buffer[offset + 4] = target_address >> 16;
+       ctx->write_buffer[offset + 5] = target_address >> 24;
+       ctx->write_buffer[offset + 6] = target_address;
+       ctx->write_buffer[offset + 7] = target_address >> 8;
+
+       ctx->write_buffer[offset + 8] = packet_length >> 16;
+       ctx->write_buffer[offset + 9] = packet_length >> 24;
+       ctx->write_buffer[offset + 10] = packet_length;
+       ctx->write_buffer[offset + 11] = packet_length >> 8;
+
+       ctx->write_buffer[offset + packet_length + COMMAND_PACKET_LENGTH - 2] = 
packet_crc;
+       ctx->write_buffer[offset + packet_length + COMMAND_PACKET_LENGTH - 1] = 
packet_crc >> 8;
+}
+
+/* Context needed by the write and read callbacks */
+struct transfer_result {
+       struct fp6_ctx *ctx;
+       bool done;
+       uint32_t transferred;
+};
+
+static LIBUSB_CALL void read_cb(struct libusb_transfer *transfer)
+{
+       struct transfer_result *res = transfer->user_data;
+       struct fp6_ctx *ctx = res->ctx;
+       uint32_t packet_size = ctx->max_packet_size;
+       /*
+        * Strip the two status bytes sent at the beginning of each USB packet
+        * while copying the chunk buffer to the read buffer
+        */
+       uint32_t num_packets = DIV_ROUND_UP(transfer->actual_length, 
packet_size);
+       uint32_t chunk_remains = transfer->actual_length;
+
+       for (uint32_t i = 0; i < num_packets && chunk_remains > 2; i++) {
+               uint32_t this_size = packet_size - 2;
+
+               if (this_size > chunk_remains - 2)
+                       this_size = chunk_remains - 2;
+               if (this_size > ctx->read_count - res->transferred)
+                       this_size = ctx->read_count - res->transferred;
+               memcpy(ctx->read_buffer + res->transferred,
+                       ctx->read_chunk + packet_size * i + 2,
+                       this_size);
+               res->transferred += this_size;
+               chunk_remains -= this_size + 2;
+               if (res->transferred == ctx->read_count) {
+                       res->done = true;
+                       break;
+               }
+       }
+
+       if (!res->done)
+               if (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)
+                       res->done = true;
+}
+
+static LIBUSB_CALL void write_cb(struct libusb_transfer *transfer)
+{
+       struct transfer_result *res = transfer->user_data;
+       struct fp6_ctx *ctx = res->ctx;
+
+       res->transferred += transfer->actual_length;
+
+       if (res->transferred == ctx->write_count) {
+               res->done = true;
+       } else {
+               transfer->length = ctx->write_count - res->transferred;
+               transfer->buffer = ctx->write_buffer + res->transferred;
+               if (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)
+                       res->done = true;
+       }
+}
+
+static void purge_usb_buffer(struct fp6_ctx *ctx)
+{
+       int err = ERROR_OK;
+
+       LOG_DEBUG("-");
+       ctx->write_count = 0;
+       ctx->read_count = 0;
+       ctx->retval = ERROR_OK;
+       bit_copy_discard(&ctx->read_queue);
+
+       LOG_WARNING("unable to purge fp6 rx buffers: %s", 
libusb_error_name(err));
+       LOG_WARNING("unable to purge fp6 tx buffers: %s", 
libusb_error_name(err));
+}
+
+static void buffer_write_byte(struct fp6_ctx *ctx, uint8_t data)
+{
+       assert(ctx->write_count < ctx->write_size);
+       ctx->write_buffer[ctx->write_count++] = data;
+}
+
+static int flush_usb_buffer(struct fp6_ctx *ctx)
+{
+       int retval = ctx->retval;
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Ignoring flush due to previous error");
+               assert(ctx->write_count == 0 && ctx->read_count == 0);
+               ctx->retval = ERROR_OK;
+               return retval;
+       }
+
+       assert(ctx->write_count > 0 || ctx->read_count == 0); /* No read data 
without write data */
+
+       if (ctx->write_count == 0)
+               return retval;
+
+       struct libusb_transfer *read_transfer = NULL;
+       struct transfer_result read_result = { .ctx = ctx, .done = true };
+
+       if (ctx->read_count) {
+               buffer_write_byte(ctx, 0x87); /* SEND_IMMEDIATE */
+               read_result.done = false;
+               /*
+                * delay read transaction to ensure the FP6 chip can support us 
with data
+                * immediately after processing the commands in the write 
transaction
+                */
+       }
+
+       struct transfer_result write_result = { .ctx = ctx, .done = false };
+       struct libusb_transfer *write_transfer = libusb_alloc_transfer(0);
+
+       libusb_fill_bulk_transfer(write_transfer, ctx->usb_dev, ctx->out_ep, 
ctx->write_buffer,
+               ctx->write_count, write_cb, &write_result, 
ctx->usb_write_timeout);
+       retval = libusb_submit_transfer(write_transfer);
+       if (retval != LIBUSB_SUCCESS)
+               goto error_check;
+
+       if (ctx->read_count) {
+               read_transfer = libusb_alloc_transfer(0);
+               libusb_fill_bulk_transfer(read_transfer, ctx->usb_dev, 
ctx->in_ep, ctx->read_chunk,
+                       ctx->read_chunk_size, read_cb, &read_result,
+                       ctx->usb_read_timeout);
+               retval = libusb_submit_transfer(read_transfer);
+               if (retval != LIBUSB_SUCCESS)
+                       goto error_check;
+       }
+
+       /* Polling loop, more or less taken from libftdi */
+       int64_t start = timeval_ms();
+       int64_t warn_after = 2000;
+
+       while (!write_result.done || !read_result.done) {
+               struct timeval timeout_usb;
+
+               timeout_usb.tv_sec = 1;
+               timeout_usb.tv_usec = 0;
+
+               retval = libusb_handle_events_timeout_completed(ctx->usb_ctx, 
&timeout_usb, NULL);
+               keep_alive();
+               if (retval == LIBUSB_ERROR_NO_DEVICE || retval == 
LIBUSB_ERROR_INTERRUPTED)
+                       break;
+
+               if (retval != LIBUSB_SUCCESS) {
+                       libusb_cancel_transfer(write_transfer);
+                       if (read_transfer)
+                               libusb_cancel_transfer(read_transfer);
+                       while (!write_result.done || !read_result.done) {
+                               retval = 
libusb_handle_events_timeout_completed(ctx->usb_ctx,
+                                                               &timeout_usb, 
NULL);
+                               if (retval != LIBUSB_SUCCESS)
+                                       break;
+                       }
+               }
+
+               int64_t now = timeval_ms();
+
+               if (now - start > warn_after) {
+                       LOG_WARNING("Haven't made progress in flushing usb 
buffer for %" PRId64
+                                       "ms.", now - start);
+                       warn_after *= 2;
+               }
+       }
+
+error_check:
+       if (retval != LIBUSB_SUCCESS) {
+               LOG_ERROR("libusb_handle_events() failed with %s", 
libusb_error_name(retval));
+               retval = ERROR_FAIL;
+       } else if (write_result.transferred < ctx->write_count) {
+               LOG_ERROR("fp6 device did not accept all data: %u, tried %u",
+                       write_result.transferred,
+                       ctx->write_count);
+               retval = ERROR_FAIL;
+       } else if (read_result.transferred < ctx->read_count) {
+               LOG_ERROR("fp6 device did not return all data: %u, expected %u",
+                       read_result.transferred,
+                       ctx->read_count);
+               retval = ERROR_FAIL;
+       } else if (ctx->read_count) {
+               ctx->write_count = 0;
+               ctx->read_count = 0;
+               bit_copy_execute(&ctx->read_queue);
+               retval = ERROR_OK;
+       } else {
+               ctx->write_count = 0;
+               bit_copy_discard(&ctx->read_queue);
+               retval = ERROR_OK;
+       }
+
+       if (retval != ERROR_OK)
+               purge_usb_buffer(ctx);
+
+       libusb_free_transfer(write_transfer);
+       if (read_transfer)
+               libusb_free_transfer(read_transfer);
+
+       return retval;
+}
+
+static int add_packet_to_usb_buffer(struct fp6_ctx *ctx, uint16_t opcode, 
uint8_t const *buf,
+                                                                       
uint32_t buf_len, uint32_t pad_len, uint32_t target_address)
+{
+       int err = ERROR_OK;
+
+       if ((COMMAND_PACKET_LENGTH + buf_len + pad_len) > ctx->write_size) {
+               LOG_ERROR("Error: Requested data size is larger than USB 
buffer");
+               return ERROR_FAIL;
+       }
+
+       if ((ctx->write_count + COMMAND_PACKET_LENGTH + buf_len + pad_len) > 
ctx->write_size) {
+               err = flush_usb_buffer(ctx);
+               if (err)
+                       return err;
+       }
+
+       if (buf)
+               memcpy(&ctx->write_buffer[ctx->write_count + 
PAYLOAD_PACKET_START_ADDRESS], buf, buf_len);
+       else
+               memset(&ctx->write_buffer[ctx->write_count + 
PAYLOAD_PACKET_START_ADDRESS], 0, buf_len);
+
+       memset(&ctx->write_buffer[ctx->write_count + 
PAYLOAD_PACKET_START_ADDRESS + buf_len], 0, pad_len);
+
+       add_usb_packet_header(ctx, FP_PACKET_START_CODE, opcode, 
target_address, buf_len + pad_len, 0xDEAD);
+
+       ctx->write_count += COMMAND_PACKET_LENGTH + buf_len + pad_len;
+
+       return err;
+}
+
+__attribute__((always_inline)) inline
+static int construct_and_send_packet(struct fp6_ctx *ctx, uint16_t opcode, 
uint8_t const *buf,
+                                                                               
                        uint32_t buf_len, uint32_t target_address)
+{
+       int err;
+
+       err = add_packet_to_usb_buffer(ctx, opcode, buf, buf_len, 0, 
target_address);
+       if (err)
+               return err;
+
+       return flush_usb_buffer(ctx);
+}
+
+static int sf2_reset(struct fp6_ctx *ctx)
+{
+       char resp[1] = { 0 };
+       int err;
+
+       err = jtag_libusb_control_transfer(ctx->usb_dev,
+                                LIBUSB_REQUEST_TYPE_VENDOR | 
LIBUSB_ENDPOINT_IN,
+                                FP6_SF2_RESET_OPCODE,
+                                ctx->value,
+                                ctx->index,
+                                resp,
+                                sizeof(resp),
+                                ctx->usb_write_timeout,
+                                NULL);
+
+       if (resp[0] != 0)
+               return (int)resp[0];
+
+       return err;
+}
+
+static int frequency_interrupt(struct fp6_ctx *ctx)
+{
+       int err;
+
+       usleep(10000);
+       err = construct_and_send_packet(ctx, FREQUENCY_INTERRUPT_OPCODE, NULL, 
0, 0);
+       usleep(10000);
+
+       return err;
+}
+
+static int get_hw_tck_frequency(struct fp6_ctx *ctx, uint8_t *freq_mhz)
+{
+       uint8_t tck_frq = 0;
+       uint8_t read_buffer[PROGRESS_PACKET_LENGTH];
+       int err;
+
+       err = construct_and_send_packet(ctx, READ_PROGRESS_OPCODE, NULL, 0, 
FP_TARGET_FREQUENCY_ADDRESS);
+       if (err)
+               return err;
+
+       err = libusb_bulk_transfer(ctx->usb_dev, ctx->in_ep, read_buffer, 
sizeof(read_buffer), 0, ctx->usb_read_timeout);
+       if (err)
+               return err;
+
+       tck_frq = read_buffer[FP_FREQUENCY_READ_BYTE_LOCATION];
+       *freq_mhz = tck_frq;
+
+       return ERROR_OK;
+}
+
+static int set_operation_status(struct fp6_ctx *ctx, enum op_status op_status)
+{
+       char resp[1] = { 0 };
+       uint16_t opcode = TURN_ACTIVITY_LED_OFF_OPCODE;
+       uint16_t request =  FP6_SET_LED_OFF_VENDOR_OPCODE;
+       int err;
+
+       switch (op_status) {
+       case OP_OFF:
+               opcode = TURN_ACTIVITY_LED_OFF_OPCODE;
+               request = FP6_SET_LED_OFF_VENDOR_OPCODE;
+               break;
+
+       case OP_PASS:
+               opcode = TURN_ACTIVITY_LED_OFF_OPCODE;
+               request = FP6_SET_LED_PASS_VENDOR_OPCODE;
+               break;
+
+       case OP_FAIL:
+               opcode = TURN_ACTIVITY_LED_OFF_OPCODE;
+               request = FP6_SET_LED_FAIL_VENDOR_OPCODE;
+               break;
+
+       case OP_ACTIVE:
+               opcode = TURN_ACTIVITY_LED_ON_OPCODE;
+               request = FP6_SET_LED_OFF_VENDOR_OPCODE;
+               break;
+
+       default:
+               break;
+       }
+
+       err =  construct_and_send_packet(ctx, opcode, NULL, 0, 0);
+       if (err)
+               return err;
+
+       err = jtag_libusb_control_transfer(ctx->usb_dev,
+                                        LIBUSB_REQUEST_TYPE_VENDOR | 
LIBUSB_ENDPOINT_IN,
+                                        request,
+                                        ctx->value,
+                                        ctx->index,
+                                        resp,
+                                        sizeof(resp),
+                                        ctx->usb_write_timeout,
+                                        NULL);
+
+       if (err)
+               return err;
+
+       if (resp[0] != 0)
+               return (int)resp[0];
+
+       return ERROR_OK;
+}
+
+#define MAX_ATTEMPTS (2)
+
+static int set_tck_frequency(struct fp6_ctx *ctx, const uint32_t hz)
+{
+       uint8_t payload_buffer[ACTION_PACKET_LENGTH] = { 0 };
+       uint8_t mhz;
+       uint8_t hw_mhz = 0;
+       int err;
+       int i;
+
+       mhz = (uint8_t)(hz / 1000000);
+
+       payload_buffer[0] = FP_SET_TCK_FREQUENCY;
+       payload_buffer[1] = mhz;
+
+       for (i = 0; i < MAX_ATTEMPTS; i++) {
+               err = construct_and_send_packet(ctx, SEND_PACKET_OPCODE, 
payload_buffer,
+                                                       ACTION_PACKET_LENGTH, 
FP_TARGET_FREQUENCY_ADDRESS);
+               if (err)
+                       return err;
+
+               err = frequency_interrupt(ctx);
+               if (err)
+                       return err;
+
+               err = get_hw_tck_frequency(ctx, &hw_mhz);
+               if (err)
+                       return err;
+
+               if (hw_mhz != mhz)
+                       LOG_WARNING("failed to set tck frequency .. retrying");
+               else
+                       break;
+       }
+
+       if (hw_mhz != mhz) {
+               LOG_ERROR("error: failed to set tck frequency");
+               return ERROR_FAIL;
+       }
+
+       err = construct_and_send_packet(ctx, ENABLE_JTAG_PORT_OPCODE, NULL, 0, 
0);
+       if (err)
+               return err;
+
+       return set_operation_status(ctx, OP_ACTIVE);
+}
+
+static int m3_reset(struct fp6_ctx *ctx, bool state)
+{
+       /* False is hold in reset */
+       if (!state)
+               return construct_and_send_packet(ctx, M3_IN_RESET_OPCODE, NULL, 
0, 0);
+       else
+               return construct_and_send_packet(ctx, M3_OUT_OF_RESET_OPCODE, 
NULL, 0, 0);
+}
+
+static int set_trst(struct fp6_ctx *ctx, bool state)
+{
+       uint8_t payload_buffer[ATOMIC_JTAG_OPERATION_PACKET_LENGTH];
+       uint8_t frame[READ_PACKET_LENGTH];
+       int err;
+
+       /* read the existing values of JTAG pins before setting trst pin */
+       err = construct_and_send_packet(ctx, GET_JTAG_PINS_OPCODE, NULL, 0, 0);
+       if (err)
+               return err;
+
+       err = libusb_bulk_transfer(ctx->usb_dev, ctx->in_ep, frame, 
sizeof(frame), 0, ctx->usb_read_timeout);
+       if (err)
+               return err;
+
+       payload_buffer[0] = frame[8];
+       payload_buffer[0] &= ~(FP6_TRSTB_BIT);
+       payload_buffer[1] = 0;
+
+       if (state == 1)
+               payload_buffer[0] |= FP6_TRSTB_BIT;
+
+       return construct_and_send_packet(ctx, SET_JTAG_PINS_OPCODE, 
payload_buffer, ATOMIC_JTAG_OPERATION_PACKET_LENGTH, 0);
+}
+
+static int set_prog_mode_pin(struct fp6_ctx *ctx, bool state)
+{
+       char resp[1] = { 0 };
+       uint16_t opcode = FP6_SET_PROG_MODE_HIGH_VENDOR_OPCODE;
+       int err;
+
+       if (state == 0)
+               opcode = FP6_SET_PROG_MODE_LOW_VENDOR_OPCODE;
+
+       err = jtag_libusb_control_transfer(ctx->usb_dev,
+                                                        
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
+                                                        opcode,
+                                                        ctx->value,
+                                                        ctx->index,
+                                                        resp,
+                                                        sizeof(resp),
+                                                        ctx->usb_write_timeout,
+                                                        NULL);
+
+       if (err)
+               return err;
+
+       if (resp[0] != 0)
+               return (int)resp[0];
+
+       return ERROR_OK;
+}
+
+static int enable_programming_port(struct fp6_ctx *ctx, bool enable)
+{
+       if (enable) {
+               construct_and_send_packet(ctx, ENABLE_JTAG_PORT_OPCODE, NULL, 
0, 0);
+
+               m3_reset(ctx, false);
+               m3_reset(ctx, true);
+
+               /* Only set TRST to HIGH when enable == true */
+               set_trst(ctx, 1);
+               set_prog_mode_pin(ctx, 1);
+       } else {
+               set_prog_mode_pin(ctx, 0);
+               construct_and_send_packet(ctx, DISABLE_JTAG_PORT_OPCODE, NULL, 
0, 0);
+       }
+
+       return ERROR_OK;
+}
+
+static int execute_delay(struct fp6_ctx *ctx, uint32_t ticks)
+{
+       uint8_t payload[4] = { 0 };
+
+       payload[0] = ticks >> 16;
+       payload[1] = ticks >> 24;
+       payload[2] = ticks;
+       payload[3] = ticks >> 8;
+
+       return add_packet_to_usb_buffer(ctx, INSERT_WAIT_CYCLES_OPCODE, 
payload, sizeof(payload), 0, 0);
+}
+
+static int jtag_delay(struct fp6_ctx *ctx, uint32_t tck, uint32_t ticks)
+{
+       return execute_delay(ctx, tck + ticks);
+}
+
+static int jtag_goto_state(struct fp6_ctx *ctx, uint16_t jtag_state)
+{
+       uint8_t payload[ATOMIC_JTAG_OPERATION_PACKET_LENGTH] = { 0 };
+       int err;
+
+       payload[0] = jtag_state;
+
+       err = add_packet_to_usb_buffer(ctx, JTAG_ATOMIC_OPCODE, payload, 
sizeof(payload), 0, 0);
+       flush_usb_buffer(ctx);
+
+       return err;
+}
+
+static inline uint32_t ROUND_UP8(uint32_t num)
+{
+       num += 7;
+       num /= 8;
+       num *= 8;
+
+       return num;
+}
+
+#define MAX_FRAME_DATA (READ_PACKET_LENGTH - 
(PAYLOAD_READ_PACKET_START_ADDRESS + PACKET_CRC_BYTE_SIZE))
+
+static int retrieve_data(struct fp6_ctx *ctx, uint8_t *in, uint32_t bitlen)
+{
+       uint8_t buffer[READ_PACKET_LENGTH];
+       uint32_t byte_len;
+       uint32_t num_frames;
+       int buf_offset = 0;
+       int frame_len;
+       uint32_t i;
+       int err;
+
+       /* convert bit_len to byte_len - round up partial bits to a byte */
+       byte_len = (bitlen + 7) / 8;
+
+       /* byte_len is rounded to a multiple of 8 bytes */
+       byte_len = ROUND_UP8(byte_len);
+
+       num_frames = (byte_len) / MAX_FRAME_DATA;
+       if ((num_frames * MAX_FRAME_DATA) < byte_len)
+               num_frames++;
+
+       err = construct_and_send_packet(ctx, READ_TDO_BUFFER_COMMAND_OPCODE, 
NULL, 0, 0);
+       if (err)
+               return err;
+
+       for (i = 0; i < num_frames; i++) {
+               err = libusb_bulk_transfer(ctx->usb_dev, ctx->in_ep, buffer, 
sizeof(buffer), 0, ctx->usb_read_timeout);
+               if (err)
+                       return err;
+
+               frame_len = byte_len > MAX_FRAME_DATA ? MAX_FRAME_DATA : 
byte_len;
+
+               memcpy(in, &buffer[PAYLOAD_READ_PACKET_START_ADDRESS + 
buf_offset], frame_len);
+               buf_offset += frame_len;
+               byte_len -= frame_len;
+       }
+
+       return ERROR_OK;
+}
+
+static int jtag_common_scan(struct fp6_ctx *ctx, int bitlen, uint8_t const 
*out, uint8_t *in, bool is_ir)
+{
+       uint8_t payload[ATOMIC_JTAG_OPERATION_PACKET_LENGTH];
+       uint32_t bytes_to_transmit;
+       uint32_t padding_bytes = 0;
+       uint8_t *dummy = NULL;
+       int err;
+
+       if (bitlen > 8064) {
+               LOG_ERROR("Error: Number of bits to shifted exceeded supported 
maximum of 8064 bits");
+               return ERROR_FAIL;
+       }
+
+       jtag_goto_state(ctx, TAP_IDLE);
+
+       payload[0] = bitlen & 0xff;
+       payload[1] = bitlen >> 8;
+
+       err = add_packet_to_usb_buffer(ctx, SET_SHIFT_IR_DR_BIT_LENGTH_OPCODE, 
payload, sizeof(payload), 0, 0);
+       if (err)
+               return err;
+
+       if (!out) {
+               bytes_to_transmit = (uint32_t)((bitlen + 7) / 8);
+               dummy = calloc(bytes_to_transmit, 1);
+               if (!dummy)
+                       return ERROR_FAIL;
+               out = dummy;
+       }
+
+       if (bitlen > 16) {
+               bytes_to_transmit = (uint32_t)((bitlen + 7) / 8);
+               if (bytes_to_transmit % 16)
+                       padding_bytes = 16 - (bytes_to_transmit % 16);
+               add_packet_to_usb_buffer(ctx, SHIFT_DATA_FROM_DDR_OPCODE, out, 
bytes_to_transmit, padding_bytes, 0);
+       } else {
+               add_packet_to_usb_buffer(ctx, SHIFT_DATA_FROM_REGISTER_OPCODE, 
out, 2, 0, 0);
+       }
+
+       if (is_ir) {
+               jtag_goto_state(ctx, TAP_IRSHIFT);
+               jtag_goto_state(ctx, TAP_IRPAUSE);
+       } else {
+               jtag_goto_state(ctx, TAP_DRSHIFT);
+               jtag_goto_state(ctx, TAP_DRPAUSE);
+       }
+
+       if (in)
+               retrieve_data(ctx, in, bitlen);
+
+       if (dummy)
+               free(dummy);
+
+       return err;
+}
+
+static int jtag_ir_scan(struct fp6_ctx *ctx, int bitlen, uint8_t const *out, 
uint8_t *in)
+{
+       return jtag_common_scan(ctx, bitlen, out, in, 1);
+}
+
+static int jtag_dr_scan(struct fp6_ctx *ctx, int bitlen, uint8_t const *out, 
uint8_t *in)
+{
+       return jtag_common_scan(ctx, bitlen, out, in, 0);
+}
+
+int fp6_get_serial_number(struct fp6_ctx *ctx, char *desc_serial, size_t 
buflen)
+{
+       uint8_t buffer[4] = { 0 };
+       int err;
+
+       err = jtag_libusb_control_transfer(ctx->usb_dev,
+                                LIBUSB_REQUEST_TYPE_VENDOR | 
LIBUSB_ENDPOINT_IN,
+                                FP6_GET_PROGRAMMER_SERIAL_NUMBER_VENDOR_OPCODE,
+                                ctx->value,
+                                ctx->index,
+                                (char *)buffer,
+                                sizeof(buffer),
+                                ctx->usb_read_timeout,
+                                NULL);
+       if (err)
+               return err;
+
+       snprintf(desc_serial, buflen, "%02X%02X%02X%02X", buffer[3], buffer[2], 
buffer[1], buffer[0]);
+
+       return ERROR_OK;
+}
+
+/*
+ * Helper to open a libusb device that matches vid, pid, product string and/or 
serial string.
+ * Set any field to 0 as a wildcard. If the device is found true is returned, 
with ctx containing
+ * the already opened handle. ctx->interface must be set to the desired 
interface (channel) number
+ * prior to calling this function
+ */
+static bool open_matching_device(struct fp6_ctx *ctx, const uint16_t vids[], 
const uint16_t pids[],
+       const char *product, const char *serial, const char *location)
+{
+       struct libusb_device **list;
+       struct libusb_device_descriptor desc;
+       struct libusb_config_descriptor *config0;
+       int err;
+       bool found = false;
+       ssize_t cnt = libusb_get_device_list(ctx->usb_ctx, &list);
+       char desc_man[32] = { 0 }; /* Max size of FP6 descriptor */
+       char desc_prod[16] = { 0 }; /* Max size of FP6 descriptor */
+       char desc_serial[9] = { 0 };
+
+       if (cnt < 0)
+               LOG_ERROR("libusb_get_device_list() failed with %s", 
libusb_error_name(cnt));
+
+       for (ssize_t i = 0; i < cnt; i++) {
+               struct libusb_device *device = list[i];
+
+               err = libusb_get_device_descriptor(device, &desc);
+               if (err != LIBUSB_SUCCESS) {
+                       LOG_ERROR("libusb_get_device_descriptor() failed with 
%s", libusb_error_name(err));
+                       continue;
+               }
+
+               if (!jtag_libusb_match_ids(&desc, vids, pids))
+                       continue;
+
+               err = libusb_open(device, &ctx->usb_dev);
+               if (err != LIBUSB_SUCCESS) {
+                       LOG_ERROR("libusb_open() failed with %s",
+                                 libusb_error_name(err));
+                       continue;
+               }
+
+               err = fp6_get_serial_number(ctx, desc_serial, 
sizeof(desc_serial));
+               if (err) {
+                       LOG_ERROR("failed to get serial number");
+                       return false;
+               }
+
+               if (location && !device_location_equal(device, location)) {
+                       libusb_close(ctx->usb_dev);
+                       continue;
+               }
+
+               if (product && !string_descriptor_equal(ctx->usb_dev, 
desc.iProduct, product)) {
+                       libusb_close(ctx->usb_dev);
+                       continue;
+               }
+
+               if (serial && strncmp(desc_serial, serial, 126)) {
+                       libusb_close(ctx->usb_dev);
+                       continue;
+               }
+
+               err = libusb_get_string_descriptor_ascii(ctx->usb_dev, 
desc.iProduct, (uint8_t *)desc_prod,
+                                                       sizeof(desc_prod));
+               if (err < 0) {
+                       LOG_ERROR("libusb_get_string_descriptor_ascii() failed 
with %s", libusb_error_name(err));
+                       return false;
+               }
+
+               err = libusb_get_string_descriptor_ascii(ctx->usb_dev, 
desc.iManufacturer, (uint8_t *)desc_man,
+                                                       sizeof(desc_man));
+               if (err < 0) {
+                       LOG_ERROR("libusb_get_string_descriptor_ascii() failed 
with %s", libusb_error_name(err));
+                       return false;
+               }
+
+               LOG_INFO("%s %s %s", desc_man, desc_prod, desc_serial);
+
+               found = true;
+               break;
+       }
+
+       libusb_free_device_list(list, 1);
+
+       if (!found) {
+               /* The caller reports detailed error desc */
+               return false;
+       }
+
+       err = libusb_get_config_descriptor(libusb_get_device(ctx->usb_dev), 0, 
&config0);
+       if (err != LIBUSB_SUCCESS) {
+               LOG_ERROR("libusb_get_config_descriptor() failed with %s", 
libusb_error_name(err));
+               libusb_close(ctx->usb_dev);
+               return false;
+       }
+
+       /* Make sure the first configuration is selected */
+       int cfg;
+
+       err = libusb_get_configuration(ctx->usb_dev, &cfg);
+       if (err != LIBUSB_SUCCESS) {
+               LOG_ERROR("libusb_get_configuration() failed with %s", 
libusb_error_name(err));
+               goto error;
+       }
+
+       if (desc.bNumConfigurations > 0 && cfg != config0->bConfigurationValue) 
{
+               err = libusb_set_configuration(ctx->usb_dev, 
config0->bConfigurationValue);
+               if (err != LIBUSB_SUCCESS) {
+                       LOG_ERROR("libusb_set_configuration() failed with %s", 
libusb_error_name(err));
+                       goto error;
+               }
+       }
+
+       err = libusb_claim_interface(ctx->usb_dev, ctx->interface);
+       if (err != LIBUSB_SUCCESS) {
+               LOG_ERROR("libusb_claim_interface() failed with %s", 
libusb_error_name(err));
+               goto error;
+       }
+
+       /* Determine maximum packet size and endpoint addresses */
+       if (!(desc.bNumConfigurations > 0 && ctx->interface < 
config0->bNumInterfaces &&
+          config0->interface[ctx->interface].num_altsetting > 0))
+               goto desc_error;
+
+       const struct libusb_interface_descriptor *descriptor;
+
+       descriptor = &config0->interface[ctx->interface].altsetting[0];
+       if (descriptor->bNumEndpoints != 2)
+               goto desc_error;
+
+       ctx->in_ep = 0;
+       ctx->out_ep = 0;
+       for (int i = 0; i < descriptor->bNumEndpoints; i++) {
+               if (descriptor->endpoint[i].bEndpointAddress & 0x80) {
+                       ctx->in_ep = descriptor->endpoint[i].bEndpointAddress;
+                       ctx->max_packet_size =
+                                       descriptor->endpoint[i].wMaxPacketSize;
+               } else {
+                       ctx->out_ep = descriptor->endpoint[i].bEndpointAddress;
+               }
+       }
+
+       if (ctx->in_ep == 0 || ctx->out_ep == 0)
+               goto desc_error;
+
+       libusb_free_config_descriptor(config0);
+       return true;
+
+desc_error:
+       LOG_ERROR("unrecognized USB device descriptor");
+error:
+       libusb_free_config_descriptor(config0);
+       libusb_close(ctx->usb_dev);
+       return false;
+}
+
+static int mchp_fp6_init(void)
+{
+       const uint16_t vids[1] = { FP6_VID };
+       const uint16_t pids[1] = { FP6_PID };
+       struct fp6_ctx *ctx = calloc(1, sizeof(*ctx));
+       char const *desc = NULL;
+       char const *ser = NULL;
+       char const *loc = NULL;
+       int err;
+
+       if (!ctx)
+               return ERROR_FAIL;
+
+       if (select_serial_num)
+               ser = serial_num;
+
+       bit_copy_queue_init(&ctx->read_queue);
+       ctx->read_chunk_size = FP_MAX_USB_BUFFER_BYTE_SIZE;
+       ctx->read_size = FP_MAX_USB_BUFFER_BYTE_SIZE;
+       ctx->write_size = FP_MAX_USB_BUFFER_BYTE_SIZE;
+       ctx->read_chunk = malloc(ctx->read_chunk_size);
+       ctx->read_buffer = malloc(ctx->read_size);
+
+       ctx->write_buffer = calloc(1, ctx->write_size);
+
+       if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer)
+               goto error;
+
+       ctx->interface = 0;
+       ctx->index = 0xfff0;
+       ctx->value = 3;
+       ctx->usb_read_timeout = FP_READ_TIMEOUT;
+       ctx->usb_write_timeout = FP_READ_TIMEOUT;
+
+       err = libusb_init(&ctx->usb_ctx);
+       if (err != LIBUSB_SUCCESS) {
+               LOG_ERROR("libusb_init() failed with %s", 
libusb_error_name(err));
+               goto error;
+       }
+
+       if (!open_matching_device(ctx, vids, pids, desc, ser, loc)) {
+               LOG_ERROR("unable to open device with description '%s', "
+                                 "serial '%s' at bus location '%s'",
+                                 desc ? desc : "*",
+                                 ser ? ser : "*",
+                                 loc ? loc : "*");
+               ctx->usb_dev = NULL;
+               goto error;
+       }
+
+       fp6_ctx = ctx;
+
+       sf2_reset(ctx);
+
+       enable_programming_port(ctx, 1);
+
+       return ERROR_OK;
+error:
+       return ERROR_FAIL;
+}
+
+static int mchp_fp6_execute_statemove(struct fp6_ctx *ctx, struct 
statemove_command const *cmd)
+{
+       return jtag_goto_state(ctx, cmd->end_state);
+}
+
+static int mchp_fp6_execute_runtest(struct fp6_ctx *ctx, struct 
runtest_command const *cmd)
+{
+       int err;
+
+       err = jtag_goto_state(ctx, TAP_IDLE);
+       if (err)
+               return err;
+
+       err = jtag_delay(ctx, cmd->num_cycles, 0);
+       if (err)
+               return err;
+
+       err = jtag_goto_state(ctx, cmd->end_state);
+       if (err)
+               return err;
+
+       return ERROR_OK;
+}
+
+static int mchp_fp6_execute_reset(struct fp6_ctx *ctx, struct reset_command 
const *cmd)
+{
+       return jtag_goto_state(ctx, TAP_RESET);
+}
+
+static int mchp_fp6_ir_scan(struct fp6_ctx *ctx, uint32_t bitlen, uint8_t 
const *out, uint8_t *in, uint16_t end_state)
+{
+       return jtag_ir_scan(ctx, bitlen, out, in);
+}
+
+static int mchp_fp6_dr_scan(struct fp6_ctx *ctx, uint32_t bitlen, uint8_t 
const *out, uint8_t *in, uint16_t end_state)
+{
+       return jtag_dr_scan(ctx, bitlen, out, in);
+}
+
+static int mchp_fp6_execute_scan(struct fp6_ctx *ctx, struct scan_command 
const *cmd)
+{
+       int error;
+
+       /*
+        * Experimentally noted that rarely the end state is something else 
than IDLE
+        * Then num_fields is 1 even when the base structure support bigger 
scans
+        */
+       if (cmd->num_fields > 1) {
+               LOG_ERROR("Execute scan is implemented only with one field but 
this request has %d fields\r", cmd->num_fields);
+               return ERROR_FAIL;
+       }
+
+       if (cmd->ir_scan)
+               error = mchp_fp6_ir_scan(ctx, cmd->fields[0].num_bits,
+                                       (uint8_t *)(cmd->fields[0].out_value), 
cmd->fields[0].in_value, cmd->end_state);
+       else
+               error = mchp_fp6_dr_scan(ctx, cmd->fields[0].num_bits,
+                                       (uint8_t *)(cmd->fields[0].out_value), 
cmd->fields[0].in_value, cmd->end_state);
+
+       return error;
+}
+
+static int mchp_fp6_speed_div(int speed, int *khz)
+{
+       uint32_t actual_speed = 0;
+       int err;
+
+       err = get_hw_tck_frequency(fp6_ctx, (uint8_t *)&actual_speed);
+       if (err)
+               return err;
+
+       /* hw_tck_freq is in MHz */
+       *khz = actual_speed * HZ_PER_KHZ;
+
+       return ERROR_OK;
+}
+
+static int mchp_fp6_khz(int khz, int *jtag_speed)
+{
+       *jtag_speed = khz * HZ_PER_KHZ;
+
+       return ERROR_OK;
+}
+
+static int mchp_fp6_execute_command(struct fp6_ctx *ctx, struct jtag_command 
const *cmd)
+{
+       switch (cmd->type) {
+       case JTAG_SCAN:
+               return mchp_fp6_execute_scan(ctx, cmd->cmd.scan);
+
+       case JTAG_RUNTEST:
+               return mchp_fp6_execute_runtest(ctx, cmd->cmd.runtest);
+
+       case JTAG_RESET:
+               return mchp_fp6_execute_reset(ctx, cmd->cmd.reset);
+
+       case JTAG_TLR_RESET:
+               return mchp_fp6_execute_statemove(ctx, cmd->cmd.statemove);
+
+       default:
+               return ERROR_FAIL;
+       }
+
+       return ERROR_FAIL;
+}
+
+static int mchp_fp6_execute_queue(struct jtag_command *cmd_queue)
+{
+       struct jtag_command *cmd = cmd_queue;
+
+       while (cmd) {
+               if (mchp_fp6_execute_command(fp6_ctx, cmd))
+                       return ERROR_COMMAND_CLOSE_CONNECTION;
+
+               cmd = cmd->next;
+       }
+
+       return ERROR_OK;
+}
+
+static int mchp_fp6_reset(int trst, int srst)
+{
+       int err;
+
+       if (trst == 0)
+               err = set_trst(fp6_ctx, 1);
+       else
+               err = set_trst(fp6_ctx, 0);
+
+       return err;
+}
+
+static int mchp_fp6_speed(int32_t tck_freq)
+{
+       return set_tck_frequency(fp6_ctx, tck_freq);
+}
+
+COMMAND_HANDLER(mchp_fp6_handle_serial_num)
+{
+       if (CMD_ARGC == 1) {
+               memcpy(serial_num, CMD_ARGV[0], 126);
+               select_serial_num = 1;
+       }
+
+       return ERROR_OK;
+}
+
+static const struct command_registration mchp_fp6_subcommand_handlers[] = {
+       {
+               .name = "serial",
+               .handler = mchp_fp6_handle_serial_num,
+               .mode = COMMAND_CONFIG,
+               .help = "SERIAL_NUM to locate unique FP6",
+               .usage = "[SERIAL_NUM]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration mchp_fp6_command_handlers[] = {
+       {
+               .name = "mchp_fp6",
+               .mode = COMMAND_ANY,
+               .help = "perform mchp_fp6 management",
+               .chain = mchp_fp6_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const char * const mchp_fp6_transports[] = { "jtag", NULL };
+
+static struct jtag_interface mchp_fp6_interface = {
+       .supported = 0, /* Don't support DEBUG_CAP_TMS_SEQ */
+       .execute_queue = mchp_fp6_execute_queue,
+};
+
+struct adapter_driver mchp_fp6_adapter_driver = {
+       .name = "microchip-fp6",
+       .commands = mchp_fp6_command_handlers,
+       .transports = mchp_fp6_transports,
+       .init = mchp_fp6_init,
+       .quit = mchp_fp6_quit,
+       .reset = mchp_fp6_reset,
+       .speed = mchp_fp6_speed,
+       .khz = mchp_fp6_khz,
+       .speed_div = mchp_fp6_speed_div,
+       .jtag_ops = &mchp_fp6_interface,
+};
diff --git a/src/jtag/drivers/mchp_fp6.h b/src/jtag/drivers/mchp_fp6.h
new file mode 100644
index 0000000000..c9bd75c301
--- /dev/null
+++ b/src/jtag/drivers/mchp_fp6.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef MCHP_FP6
+#define MCHP_FP6
+
+#include "mchp_fp.h"
+
+#define FP6_VID 0x1514
+#define FP6_PID 0x2009
+
+#define PAYLOAD_PACKET_START_ADDRESS                   12
+#define PAYLOAD_READ_PACKET_START_ADDRESS              8
+
+#define PACKET_CRC_BYTE_SIZE                           2
+
+#define FP6_TRSTB_BIT                                  0x4
+
+enum op_status {
+       OP_UNDEF,
+       OP_OFF,
+       OP_PASS,
+       OP_FAIL,
+       OP_ACTIVE,
+       OP_MAX
+};
+
+#endif
diff --git a/src/jtag/interface.h b/src/jtag/interface.h
index b448851dc4..64ebc48f03 100644
--- a/src/jtag/interface.h
+++ b/src/jtag/interface.h
@@ -403,5 +403,6 @@ extern struct adapter_driver vdebug_adapter_driver;
 extern struct adapter_driver vsllink_adapter_driver;
 extern struct adapter_driver xds110_adapter_driver;
 extern struct adapter_driver xlnx_pcie_xvc_adapter_driver;
+extern struct adapter_driver mchp_fp6_adapter_driver;
 
 #endif /* OPENOCD_JTAG_INTERFACE_H */
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index c24ead8cd9..1f376ec966 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -152,6 +152,9 @@ struct adapter_driver *adapter_drivers[] = {
 #endif
 #if BUILD_AM335XGPIO == 1
                &am335xgpio_adapter_driver,
+#endif
+#if BUILD_MICROCHIP_FP6 == 1
+               &mchp_fp6_adapter_driver,
 #endif
                NULL,
        };
diff --git a/tcl/board/microchip_riscv_fp6.cfg 
b/tcl/board/microchip_riscv_fp6.cfg
new file mode 100644
index 0000000000..0ab8d4827a
--- /dev/null
+++ b/tcl/board/microchip_riscv_fp6.cfg
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#**************************************************************************
+#   Copyright (C) 2015-2018 by Microchip Technology Inc.                  *
+#   http://www.microchip.com/support                                      *
+#                                                                         *
+#   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/>. *
+#**************************************************************************
+
+#------------------------------------------------------------------------------
+# Microchip RISC-V board
+#------------------------------------------------------------------------------
+
+# Device
+source [find interface/flashpro6.cfg]
+source [find target/microchip_riscv.cfg]
+
+# Board specific initialization
+proc do_board_reset_init {} {
+    # doesn't appear to be called
+}
+
diff --git a/tcl/interface/flashpro6.cfg b/tcl/interface/flashpro6.cfg
new file mode 100644
index 0000000000..c6ebcdbecc
--- /dev/null
+++ b/tcl/interface/flashpro6.cfg
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# FlashPro6
+#
+adapter driver microchip-fp6
+
+# For a specific FP6
+# mchp_fp6 serial 016B047E
+
+adapter speed 6000
diff --git a/tcl/target/microchip_riscv.cfg b/tcl/target/microchip_riscv.cfg
new file mode 100644
index 0000000000..9354bcf64b
--- /dev/null
+++ b/tcl/target/microchip_riscv.cfg
@@ -0,0 +1,410 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#------------------------------------------------------------------------------
+# Microchip RISC-V
+#------------------------------------------------------------------------------
+
+# PolarFire SoC (MPFS) hart id to name lookup table
+array set mpfs_hart_names {
+       0       hart0_e51
+       1       hart1_u54_1
+       2       hart2_u54_2
+       3       hart3_u54_3
+       4       hart4_u54_4
+}
+
+# PIC64GX hart id to name lookup table
+array set pic64gx_hart_names {
+       0       hart0_e51
+       1       hart1_u54_1
+       2       hart2_u54_2
+       3       hart3_u54_3
+       4       hart4_u54_4
+}
+
+# Device tapname to IDCODE lookup tables
+
+# MPFS devices table
+set mpfs_cpu_tap_info {
+       MPFS025  0x0f8531cf
+       MPFS095  0x0f8181cf
+       MPFS160  0x0f8191cf
+       MPFS250  0x0f81a1cf
+       MPFS460  0x0f81b1cf
+       RTPFS160 0x0f8991cf
+       RTPFS460 0x0f89b1cf
+}
+
+# PIC64GX table
+set pic64gx_tap_info {
+       PIC64GX1000 0x0f8531cf
+}
+
+# PolarFire table
+set polarfire_tap_info {
+       MPF050     0x0f8101cf
+       MPF100     0x0f8111cf
+       MPF200     0x0f8121cf
+       MPF300     0x0f8131cf
+       MPF500     0x0f8141cf
+       RTPF500-A  0x8f8141cf
+       RTPF500    0x0f8941cf
+       MPFS250  0x0f81a1cf
+}
+
+# RTG4 table
+set rtg4_tap_info {
+       RT4P6000  0x007001cf
+       RT4P12000 0x007011cf
+       RT4P16000 0x007021cf
+}
+
+# MiV Soft Processor table
+set miv_tap_info {
+       MiV1 0x10e31913
+       MiV2 0x1c0011cf
+       MiV3 0x1e50105f
+}
+
+proc expected_ids {tap_list} {
+       set str ""
+       dict for {key value} $tap_list {
+               append str "-expected-id" " " $value " "
+       }
+
+       return $str
+}
+
+proc get_tap_type {idcode_in tap_list} {
+       set str "unknown"
+       set idcode "0x"
+       append idcode $idcode_in
+
+       dict for {key value} $tap_list {
+               if {$value == $idcode} {
+                       set str ""
+                       append str $key " " "TAP"
+               }
+       }
+
+       return $str
+}
+
+# Process DEVICE variable
+if {![exists DEVICE]} {
+       # Not set so default to the generic FPGA
+       set DEVICE "fpga"
+} else {
+       # Normalise - convert to lowercase
+       set DEVICE [string tolower $DEVICE]
+}
+
+# Treat "G5SOC" as legacy synonym for "MPFS"
+if {[string range $DEVICE 0 4] eq "g5soc"} {
+       set DEVICE "mpfs"
+}
+
+# Process COREID variable
+if {![exists COREID]} {
+       set COREID -1
+}
+
+# COREID
+# For Multi-Hart setups, check COREID, default to -1 (single debug connection
+# to all harts) if unspecified or invalid (out of range )
+proc handle_coreid {lower_bound upper_bound coreid} {
+       if {$coreid < $lower_bound || $coreid > $upper_bound} {
+               echo [format "Warn : COREID %s not in range 
($lower_bound..$upper_bound)" $coreid]
+               echo [format "Warn : defaulting to -1 (single connection to all 
harts)"]
+               set coreid -1
+       }
+
+       return $coreid
+}
+
+proc setup_tunnelled_miv {device fpga_tap_info soft_cpu_tap_info} {
+       # TAP management event handlers/procedures
+
+       # Switch from FPGA TAP to MiV CPU DTM TAP (by tunnelling JTAG via UJTAG)
+       proc switch_from_fpga_tap_to_riscv_tap {fpga_tap riscv_tap} {
+               jtag tapdisable $fpga_tap
+               jtag tapenable $riscv_tap
+       }
+
+       # Disable/bypass FPGA TAP
+       proc disable_fpga_tap {fpga_tap} {
+               # Tell UJTAG/uj_jtag to address MiV CPU DTM
+               irscan $fpga_tap 0x55 -endstate IRPAUSE
+               runtest 8
+
+               # Enable ujtag tunnelled JTAG - ir width 5
+               riscv use_bscan_tunnel 5 2
+       }
+
+       # Enable MiV CPU DTM TAP
+       proc enable_riscv_tap {} {
+               # Nothing to do here - the work is done in disable_fpga_tap
+               # but the handler still needs to be defined
+       }
+
+       # FPGA TAP present, occludes MiV CPU DTM TAP and must be
+       # disabled/bypassed first
+
+       # Parent FPGA TAP
+       set irlen 8
+       set expected_ids [expected_ids $fpga_tap_info]
+       eval jtag newtap $device tap -irlen $irlen $expected_ids -ignore-version
+       jtag configure $device.tap -event tap-disable "disable_fpga_tap 
$device.tap"
+       jtag configure $device.tap -event setup 
"switch_from_fpga_tap_to_riscv_tap $device.tap $device.cpu"
+
+       # Tunnelled MiV CPU DTM TAP - disabled/not visible by default until 
FPGA TAP
+       # disabled/bypassed
+       set irlen 5
+       set expected_ids [expected_ids $soft_cpu_tap_info]
+       eval jtag newtap $device cpu -irlen 5 $expected_ids -ignore-version 
-disable
+       jtag configure $device.cpu -event tap-enable enable_riscv_tap
+       # Mi-V soft core - implicitly single hart
+       set _TARGETNAME_0 $device.miv
+       target create $_TARGETNAME_0 riscv -chain-position $device.cpu
+
+       $_TARGETNAME_0 configure -event reset-init board_reset_init
+       $_TARGETNAME_0 configure -event reset-init init_regs
+
+       # gdb-detach event handler
+       $_TARGETNAME_0 configure -event gdb-detach {
+               # resume execution on debugger detach
+               resume
+       }
+
+       #
+       # Utility procedures
+       #
+
+       proc board_reset_init {} {
+               # call board level reset-init if defined
+               if {[exists -proc do_board_reset_init]} {
+                       do_board_reset_init
+               }
+       }
+
+       proc init_regs {} {
+               reg pc 0
+       }
+
+       #
+       # Reset configuration
+       #
+
+       # Only TRSTn supported
+       reset_config trst_only
+}
+
+# "Direct" debugging (not via FPGA TAP) to MiV
+proc setup_miv {device tap_info} {
+       set irlen 5
+       set expected_ids [expected_ids $tap_info]
+
+       # RISC-V CPU DTM TAP
+       eval jtag newtap $device cpu -irlen $irlen $expected_ids -ignore-version
+       # Mi-V soft core - implicitly single hart
+       set _TARGETNAME_0 $device.miv
+       target create $_TARGETNAME_0 riscv -chain-position $device.cpu
+
+       $_TARGETNAME_0 configure -event reset-init board_reset_init
+       $_TARGETNAME_0 configure -event reset-init init_regs
+
+       # gdb-detach event handler
+       $_TARGETNAME_0 configure -event gdb-detach {
+               # resume execution on debugger detach
+               resume
+       }
+
+       #
+       # Utility procedures
+       #
+
+       proc board_reset_init {} {
+               # call board level reset-init if defined
+               if {[exists -proc do_board_reset_init]} {
+                       do_board_reset_init
+               }
+       }
+
+       proc init_regs {} {
+               reg pc 0
+       }
+
+       #
+       # Reset configuration
+       #
+
+       # Only TRSTn supported
+       reset_config trst_only
+}
+
+# MPFS
+proc setup_mpfs {device coreid tap_info hart_names} {
+       set coreid [handle_coreid -1 4 $coreid ]
+
+       # RISC-V CPU DTM TAP
+       set irlen 8
+       set expected_ids [expected_ids $tap_info]
+       eval jtag newtap $device cpu -irlen $irlen $expected_ids -ignore-version
+
+       # PolarFire SoC (MPFS)
+       if {$coreid == -1} {
+               # Single debug connection to all harts
+               set _TARGETNAME_0 $device.$hart_names(0)
+               set _TARGETNAME_1 $device.$hart_names(1)
+               set _TARGETNAME_2 $device.$hart_names(2)
+               set _TARGETNAME_3 $device.$hart_names(3)
+               set _TARGETNAME_4 $device.$hart_names(4)
+
+               target create $_TARGETNAME_0 riscv -chain-position $device.cpu 
-coreid 0 -rtos hwthread
+               target create $_TARGETNAME_1 riscv -chain-position $device.cpu 
-coreid 1
+               target create $_TARGETNAME_2 riscv -chain-position $device.cpu 
-coreid 2
+               target create $_TARGETNAME_3 riscv -chain-position $device.cpu 
-coreid 3
+               target create $_TARGETNAME_4 riscv -chain-position $device.cpu 
-coreid 4
+               target smp $_TARGETNAME_0 $_TARGETNAME_1 $_TARGETNAME_2 
$_TARGETNAME_3 $_TARGETNAME_4
+
+               $_TARGETNAME_1 configure -event reset-init init_regs
+               $_TARGETNAME_2 configure -event reset-init init_regs
+               $_TARGETNAME_3 configure -event reset-init init_regs
+               $_TARGETNAME_4 configure -event reset-init init_regs
+       } else {
+               # Debug connection to a specific hart
+               set _TARGETNAME_0 $device.$hart_names($coreid)
+               target create $_TARGETNAME_0 riscv -chain-position $device.cpu 
-coreid $coreid
+       }
+
+       # Read RISC-V CPU DTM IDCODE to identify the specific part
+       init
+       irscan $device.cpu 0xf
+       set idcode [string tolower [drscan $device.cpu 32 0]]
+       echo [format "Info : Attached to %s" [get_tap_type $idcode $tap_info]]
+
+       $_TARGETNAME_0 configure -event reset-init board_reset_init
+       $_TARGETNAME_0 configure -event reset-init init_regs
+
+       # gdb-detach event handler
+       $_TARGETNAME_0 configure -event gdb-detach {
+               # resume execution on debugger detach
+               resume
+       }
+
+       #
+       # Utility procedures
+       #
+
+       proc board_reset_init {} {
+               # call board level reset-init if defined
+               if {[exists -proc do_board_reset_init]} {
+                       do_board_reset_init
+               }
+       }
+
+       proc init_regs {} {
+               reg pc 0
+       }
+
+       #
+       # Reset configuration
+       #
+
+       # Only TRSTn supported
+       reset_config trst_only
+}
+
+proc setup_pic64gx {device coreid tap_info hart_names } {
+       set coreid [handle_coreid -1 4 $coreid ]
+
+       # RISC-V CPU DTM TAP
+       set irlen 8
+       set expected_ids [expected_ids $tap_info]
+       eval jtag newtap $device cpu -irlen $irlen $expected_ids -ignore-version
+
+       # PIC64GX
+       if {$coreid == -1} {
+               # Single debug connection to all harts
+               set _TARGETNAME_0 $device.$hart_names(0)
+               set _TARGETNAME_1 $device.$hart_names(1)
+               set _TARGETNAME_2 $device.$hart_names(2)
+               set _TARGETNAME_3 $device.$hart_names(3)
+               set _TARGETNAME_4 $device.$hart_names(4)
+
+               target create $_TARGETNAME_0 riscv -chain-position $device.cpu 
-coreid 0 -rtos hwthread
+               target create $_TARGETNAME_1 riscv -chain-position $device.cpu 
-coreid 1
+               target create $_TARGETNAME_2 riscv -chain-position $device.cpu 
-coreid 2
+               target create $_TARGETNAME_3 riscv -chain-position $device.cpu 
-coreid 3
+               target create $_TARGETNAME_4 riscv -chain-position $device.cpu 
-coreid 4
+               target smp $_TARGETNAME_0 $_TARGETNAME_1 $_TARGETNAME_2 
$_TARGETNAME_3 $_TARGETNAME_4
+
+               $_TARGETNAME_1 configure -event reset-init init_regs
+               $_TARGETNAME_2 configure -event reset-init init_regs
+               $_TARGETNAME_3 configure -event reset-init init_regs
+               $_TARGETNAME_4 configure -event reset-init init_regs
+       } else {
+               # Debug connection to a specific hart
+               set _TARGETNAME_0 $device.$hart_names($coreid)
+               target create $_TARGETNAME_0 riscv -chain-position $device.cpu 
-coreid $coreid
+       }
+
+       # Read RISC-V CPU DTM IDCODE to identify the specific part
+       init
+       irscan $device.cpu 0xf
+       set idcode [string tolower [drscan $device.cpu 32 0]]
+       echo [format "Info : Attached to %s" [get_tap_type $idcode $tap_info]]
+
+       $_TARGETNAME_0 configure -event reset-init board_reset_init
+       $_TARGETNAME_0 configure -event reset-init init_regs
+
+       # gdb-detach event handler
+       $_TARGETNAME_0 configure -event gdb-detach {
+               # resume execution on debugger detach
+               resume
+       }
+
+       #
+       # Utility procedures
+       #
+
+       proc board_reset_init {} {
+               # call board level reset-init if defined
+               if {[exists -proc do_board_reset_init]} {
+                       do_board_reset_init
+               }
+       }
+
+       proc init_regs {} {
+               reg pc 0
+       }
+
+       #
+       # Reset configuration
+       #
+
+       # Only TRSTn supported
+       reset_config trst_only
+}
+
+#
+# Handle different riscv based devices
+#
+if {[string range $DEVICE 0 7] eq "pic64gx"} {
+       echo [format "Info : Connecting to member of PIC64GX family"]
+       set ret [setup_pic64gx $DEVICE $COREID $pic64gx_tap_info 
$pic64gx_hart_names]
+} elseif {[string range $DEVICE 0 4] eq "mpfs"} {
+       echo [format "Info : Connecting to member of PolarFire SoC family"]
+       set ret [setup_mpfs $DEVICE $COREID $mpfs_cpu_tap_info $mpfs_hart_names]
+} elseif {[string range $DEVICE 0 4] eq "fpga"} {
+       echo [format "Info : Connecting via ujtag tunnel to soft MiV processor 
on FPGA"]
+       # limit Soft CPU FPGAs
+       set fpga_tap_info [concat $polarfire_tap_info $rtg4_tap_info]
+       set ret [setup_tunnelled_miv $DEVICE $fpga_tap_info $miv_tap_info]
+} elseif {[string range $DEVICE 0 3] eq "miv"} {
+       echo [format "Info : Connecting to soft MiV processor on FPGA"]
+       set ret [setup_miv $DEVICE $miv_tap_info]
+} else {
+       echo [format "Error: Unknown DEVICE %s" $DEVICE]
+}
+

-- 

Reply via email to