This is an automated email from Gerrit.

"Michael Hough <dejitaru...@gmail.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6978

-- gerrit

commit f5b61fac19ed9b64d564425cb76cbed5c7323cd4
Author: Michael Hough <dejitaru...@gmail.com>
Date:   Mon May 16 12:24:24 2022 -0400

    topic: Add SWD support to FT232R driver
    
    Adds SWD protocol support to the existing FT232R JTAG driver.
    Implements bitbang.c/bitbang.h for backing logic; new code only needs to 
toggle GPIO accordingly.
    Minor clarifications in comments & code ('0' to 'NULL' when it's a pointer)
    
    Signed-off-by: Michael Hough <dejitaru...@gmail.com>
    Change-Id: Iabced6ee2b81d1f5407cde9244b7927fe7d25714

diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c
index c930d8c4c1..e296a7e4f7 100644
--- a/src/jtag/drivers/ft232r.c
+++ b/src/jtag/drivers/ft232r.c
@@ -16,6 +16,9 @@
  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
+/* 2022-05: SWD support added by Michael Hough;
+ * strongly inspired by bcm2835gpio.c, implements bitbang.c driver */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -31,6 +34,7 @@
 #include <jtag/commands.h>
 #include <helper/time_support.h>
 #include "libusb_helper.h"
+#include "bitbang.h"
 
 /* system includes */
 #include <string.h>
@@ -50,7 +54,7 @@
 #define IN_EP                  0x02
 #define OUT_EP                 0x81
 
-/* Requests */
+/* Control request types: */
 #define SIO_RESET              0 /* Reset the port */
 #define SIO_MODEM_CTRL         1 /* Set the modem control register */
 #define SIO_SET_FLOW_CTRL      2 /* Set flow control register */
@@ -63,10 +67,16 @@
 #define SIO_GET_LATENCY_TIMER  10
 #define SIO_SET_BITMODE                11
 #define SIO_READ_PINS          12
+
 #define SIO_READ_EEPROM                0x90
 #define SIO_WRITE_EEPROM       0x91
 #define SIO_ERASE_EEPROM       0x92
 
+/* Reset requests: */
+#define SIO_RESET_SIO          0 /* Reset device */
+#define SIO_RESET_PURGE_RX     1 /* Reset USB RX (host->FT232R) buffer */
+#define SIO_RESET_PURGE_TX     2 /* Reset USB TX (FT232R->host) buffer */
+
 #define FT232R_BUF_SIZE_EXTRA  4096
 
 static uint16_t ft232r_vid = 0x0403; /* FTDI */
@@ -86,23 +96,117 @@ static char *ft232r_bit_name_array[FT232R_BIT_COUNT] = {
        "RTS", /* 2: pin 3  TDO input */
        "CTS", /* 3: pin 11 TMS output */
        "DTR", /* 4: pin 2  /TRST output */
-       "DSR", /* 5: pin 9  unused */
+       "DSR", /* 5: pin 9  SWDIO */
        "DCD", /* 6: pin 10 /SYSRST output */
-       "RI"   /* 7: pin 6  unused */
+       "RI"   /* 7: pin 6  SWCLK */
 };
 
-static int tck_gpio; /* initialized to 0 by default */
+static int tck_gpio; /* = 0;*/
 static int tdi_gpio = 1;
 static int tdo_gpio = 2;
 static int tms_gpio = 3;
 static int ntrst_gpio = 4;
 static int nsysrst_gpio = 6;
+static int swdio_gpio = 5;
+static int swclk_gpio = 7;
+/* FIXME: It might make more sense to share clock pin and put SWDIO on TMS?
+ * These protocols can not be used at the same time, and it's already sharing 
reset pins. */
+
 static size_t ft232r_buf_size = FT232R_BUF_SIZE_EXTRA;
-/** 0xFFFF disables restore by default, after exit serial port will not work.
- *  0x15 sets TXD RTS DTR as outputs, after exit serial port will continue to 
work.
- */
+
+/* 0xFFFF disables restore by default, after exit serial port will not work.
+ * Any variant of 0x00nn should reset to default serial mode. */
 static uint16_t ft232r_restore_bitmode = 0xFFFF;
 
+static uint32_t swd_delay = 100;
+
+static int ft232r_swdio_read(void);
+static void ft232r_swdio_drive(bool is_output);
+static int ft232r_swd_write(int swclk, int swdio);
+
+/* Only implements SWD via bitbang driver - existing JTAG implementation seems 
faster. */
+static struct bitbang_interface ft232r_bitbang = {
+       .read = NULL,
+       .write = NULL,
+       .swdio_read = ft232r_swdio_read,
+       .swdio_drive = ft232r_swdio_drive,
+       .swd_write = ft232r_swd_write,
+       .blink = NULL /* Even if we had enough pins, can't blink without 
messing up I/O */
+};
+
+/**
+ * Sets GPIO to synchronous bitbang mode (0x0400) with the given direction 
mask.
+ * 1 is output, 0 is input.
+ */
+static int ft232r_set_dirs(unsigned char dir_mask)
+{
+       return jtag_libusb_control_transfer(adapter,
+               LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
+               SIO_SET_BITMODE, dir_mask | 0x0400,
+               0, NULL, 0, 1000);
+}
+
+/**
+ * Sets the designated SWCLK and SWDIO pins according to provided params.
+ * (swclk and swdio are ensured as boolean 0 or 1 in bitbang.c)
+ */
+static int ft232r_swd_write(int swclk, int swdio)
+{
+       char level_mask = (swclk << swclk_gpio) | (swdio << swdio_gpio);
+
+       static int runs;
+       ++runs;
+
+       int n;
+       if (jtag_libusb_bulk_write(adapter, IN_EP, &level_mask, 1, 1000, &n) != 
ERROR_OK) {
+               LOG_ERROR("usb bulk write failed");
+               return ERROR_JTAG_DEVICE_ERROR;
+       }
+
+       struct timespec ts;
+       ts.tv_sec = 0;
+       ts.tv_nsec = swd_delay;
+       nanosleep(&ts, &ts);
+
+       if (runs > 127) {
+               /* This is needed to prevent Sync BB mode from seizing up.
+               Each write also reads to internal buffer (256 bytes); stops 
when that's full.
+               Telling it to nuke buffer is much, much faster than actually 
reading. */
+               jtag_libusb_control_transfer(adapter,
+                       LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
+                       SIO_RESET, SIO_RESET_PURGE_TX, 0, NULL, 0, 1000);
+               runs = 0;
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Sets whether the SWDIO pin will be driven by host (output) or target (input)
+ */
+static void ft232r_swdio_drive(bool is_output)
+{
+       unsigned char dir_mask = (1<<swclk_gpio) | (is_output<<swdio_gpio);
+       ft232r_set_dirs(dir_mask);
+}
+
+/**
+ * Reads value on SWDIO pin.
+ * This uses immediate polling, bypassing device buffer.
+ * We are trusting that bitbang.c has put the pin into input mode by now.
+ */
+static int ft232r_swdio_read(void)
+{
+       unsigned char swdio_mask = (1 << swdio_gpio);
+       char data = 0;
+       jtag_libusb_control_transfer(adapter,
+               LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_IN,
+               SIO_READ_PINS, 0,
+               0, &data, 1, 1000);
+
+       return (data & swdio_mask) != 0;
+}
+
 /**
  * Perform sync bitbang output/input transaction.
  * Before call, an array ft232r_output[] should be filled with data to send.
@@ -210,7 +314,7 @@ static void ft232r_write(int tck, int tms, int tdi)
  * Control /TRST and /SYSRST pins.
  * Perform immediate bitbang transaction.
  */
-static void ft232r_reset(int trst, int srst)
+static int ft232r_reset(int trst, int srst)
 {
        unsigned out_value = (1<<ntrst_gpio) | (1<<nsysrst_gpio);
        LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst);
@@ -230,11 +334,12 @@ static void ft232r_reset(int trst, int srst)
        if (ft232r_output_len >= ft232r_buf_size) {
                /* FIXME: should we just execute queue here? */
                LOG_ERROR("ft232r_write: buffer overflow");
-               return;
+               return ERROR_JTAG_DEVICE_ERROR;
        }
 
        ft232r_output[ft232r_output_len++] = out_value;
        ft232r_send_recv();
+       return ERROR_OK;
 }
 
 static int ft232r_speed(int divisor)
@@ -244,9 +349,12 @@ static int ft232r_speed(int divisor)
                3000000 / divisor;
        LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud);
 
+       /* Manual delay, in nanoseconds: */
+       swd_delay = 1000000 / baud;
+
        if (jtag_libusb_control_transfer(adapter,
                LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
-               SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) {
+               SIO_SET_BAUD_RATE, divisor, 0, NULL, 0, 1000) != 0) {
                LOG_ERROR("cannot set baud rate");
                return ERROR_JTAG_DEVICE_ERROR;
        }
@@ -255,6 +363,8 @@ static int ft232r_speed(int divisor)
 
 static int ft232r_init(void)
 {
+       bitbang_interface = &ft232r_bitbang;
+
        uint16_t avids[] = {ft232r_vid, 0};
        uint16_t apids[] = {ft232r_pid, 0};
        if (jtag_libusb_open(avids, apids, &adapter, NULL)) {
@@ -263,6 +373,7 @@ static int ft232r_init(void)
                        ft232r_vid, ft232r_pid, (!ft232r_serial_desc) ? "[any]" 
: ft232r_serial_desc);
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Device found");
 
        if (ft232r_restore_bitmode == 0xFFFF) /* serial port will not be 
restored after jtag: */
                libusb_detach_kernel_driver(adapter, 0);
@@ -273,60 +384,61 @@ static int ft232r_init(void)
                LOG_ERROR("unable to claim interface");
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Interface claimed");
 
        /* Reset the device. */
        if (jtag_libusb_control_transfer(adapter,
                LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
-               SIO_RESET, 0, 0, 0, 0, 1000) != 0) {
+               SIO_RESET, SIO_RESET_SIO, 0, NULL, 0, 1000) != 0) {
                LOG_ERROR("unable to reset device");
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Device reset");
 
-       /* Sync bit bang mode. */
-       if (jtag_libusb_control_transfer(adapter,
-               LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
-               SIO_SET_BITMODE, (1<<tck_gpio) | (1<<tdi_gpio) | (1<<tms_gpio) 
| (1<<ntrst_gpio) | (1<<nsysrst_gpio) | 0x400,
-               0, 0, 0, 1000) != 0) {
+       /* Sync bit bang mode. TDO is the only input to start. */
+       unsigned char dir_mask = ~(1<<tdo_gpio);
+       if (ft232r_set_dirs(dir_mask) != 0) {
                LOG_ERROR("cannot set sync bitbang mode");
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Bitbang mode enabled");
 
        /* Exactly 500 nsec between updates. */
        unsigned divisor = 1;
        unsigned char latency_timer = 1;
 
        /* Frequency divisor is 14-bit non-zero value. */
-       if (jtag_libusb_control_transfer(adapter,
-               LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
-               SIO_SET_BAUD_RATE, divisor,
-               0, 0, 0, 1000) != 0) {
-               LOG_ERROR("cannot set baud rate");
+       if (ft232r_speed(divisor) != ERROR_OK)
                return ERROR_JTAG_INIT_FAILED;
-       }
+
+       LOG_DEBUG("Baud rate set");
+
        if (jtag_libusb_control_transfer(adapter,
                LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
-               SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) {
+               SIO_SET_LATENCY_TIMER, latency_timer, 0, NULL, 0, 1000) != 0) {
                LOG_ERROR("unable to set latency timer");
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Latency set");
 
        ft232r_output = malloc(ft232r_buf_size);
        if (!ft232r_output) {
                LOG_ERROR("Unable to allocate memory for the buffer");
                return ERROR_JTAG_INIT_FAILED;
        }
+       LOG_DEBUG("Buffer allocated");
 
        return ERROR_OK;
 }
 
 static int ft232r_quit(void)
 {
-       /* to restore serial port: set TXD RTS DTR as outputs, others as 
inputs, disable sync bit bang mode. */
+       /* Disabling bit-bang mode will resume normal serial operations. */
        if (ft232r_restore_bitmode != 0xFFFF) {
                if (jtag_libusb_control_transfer(adapter,
                        LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | 
LIBUSB_ENDPOINT_OUT,
                        SIO_SET_BITMODE, ft232r_restore_bitmode,
-                       0, 0, 0, 1000) != 0) {
+                       0, NULL, 0, 1000) != 0) {
                        LOG_ERROR("cannot set bitmode to restore serial port");
                }
        }
@@ -537,6 +649,38 @@ COMMAND_HANDLER(ft232r_handle_srst_num_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(ft232r_handle_swdio_num_command)
+{
+       if (CMD_ARGC == 1)
+               swdio_gpio = ft232r_bit_name_to_number(CMD_ARGV[0]);
+       else if (CMD_ARGC != 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (swdio_gpio < 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       command_print(CMD,
+                       "FT232R num: SWDIO = %d %s", swdio_gpio, 
ft232r_bit_number_to_name(swdio_gpio));
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ft232r_handle_swclk_num_command)
+{
+       if (CMD_ARGC == 1)
+               swclk_gpio = ft232r_bit_name_to_number(CMD_ARGV[0]);
+       else if (CMD_ARGC != 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (swclk_gpio < 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       command_print(CMD,
+                       "FT232R num: SWCLK = %d %s", swclk_gpio, 
ft232r_bit_number_to_name(swclk_gpio));
+
+       return ERROR_OK;
+}
+
 COMMAND_HANDLER(ft232r_handle_restore_serial_command)
 {
        if (CMD_ARGC == 1)
@@ -608,6 +752,20 @@ static const struct command_registration 
ft232r_subcommand_handlers[] = {
                .help = "gpio number for trst.",
                .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>",
        },
+       {
+               .name = "swdio_num",
+               .handler = ft232r_handle_swdio_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for swdio.",
+               .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>",
+       },
+       {
+               .name = "swclk_num",
+               .handler = ft232r_handle_swclk_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for swclk.",
+               .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>",
+       },
        {
                .name = "restore_serial",
                .handler = ft232r_handle_restore_serial_command,
@@ -660,8 +818,7 @@ static void syncbb_state_move(int skip)
 }
 
 /**
- * Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG
- * (or SWD) state machine.
+ * Clock a bunch of TMS transitions, to change the JTAG state machine.
  */
 static int syncbb_execute_tms(struct jtag_command *cmd)
 {
@@ -906,6 +1063,8 @@ static int syncbb_execute_queue(void)
        return retval;
 }
 
+static const char * const f232r_transports[] = { "jtag", "swd", NULL };
+
 static struct jtag_interface ft232r_interface = {
        .supported = DEBUG_CAP_TMS_SEQ,
        .execute_queue = syncbb_execute_queue,
@@ -913,14 +1072,16 @@ static struct jtag_interface ft232r_interface = {
 
 struct adapter_driver ft232r_adapter_driver = {
        .name = "ft232r",
-       .transports = jtag_only,
+       .transports = f232r_transports,
        .commands = ft232r_command_handlers,
 
        .init = ft232r_init,
        .quit = ft232r_quit,
+       .reset = ft232r_reset,
        .speed = ft232r_speed,
        .khz = ft232r_khz,
        .speed_div = ft232r_speed_div,
 
        .jtag_ops = &ft232r_interface,
+       .swd_ops = &bitbang_swd,
 };

-- 

Reply via email to