This is an automated email from Gerrit.

"Richard Pasek <rpa...@google.com>" just uploaded a new patch set to Gerrit, 
which you can find at https://review.openocd.org/c/openocd/+/8645

-- gerrit

commit 4343e856172deb6d8f50e4d9e144f575b31e655d
Author: Richard Pasek <rpa...@google.com>
Date:   Wed Dec 11 00:43:57 2024 -0500

    Add Linux SPI device SWD adapter support
    
    To alleviate the need to bitbang SWD, I've written a SWD SPI
    implementation. This code is inspired by the work of lu...@appkaki.com
    as shown at github.com/lupyuen/openocd-spi but with the desire to be
    more generic. This implementation makes use of the more common 4 wire
    SPI port using full duplex transfers to be able to capture the SWD ACK
    bits when a SWD TX operation is in progress.
    
    Change-Id: Ic2f38a1806085d527e6f999a3d15aea6f32d1019
    Signed-off-by: rpa...@google.com

diff --git a/configure.ac b/configure.ac
index 567152b0a6..8adf178204 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,6 +163,9 @@ m4_define([PCIE_ADAPTERS],
 m4_define([SERIAL_PORT_ADAPTERS],
        [[[buspirate], [Bus Pirate], [BUS_PIRATE]]])
 
+m4_define([LINUXSPIDEV_ADAPTER],
+       [[[linuxspidev], [Linux SPI DEV driver], [LINUXSPIDEV]]])
+
 # The word 'Adapter' in "Dummy Adapter" below must begin with a capital letter
 # because there is an M4 macro called 'adapter'.
 m4_define([DUMMY_ADAPTER],
@@ -294,6 +297,8 @@ AC_ARG_ADAPTERS([
   LIBJAYLINK_ADAPTERS
   ],[auto])
 
+AC_ARG_ADAPTERS([LINUXSPIDEV_ADAPTER],[no])
+
 AC_ARG_ADAPTERS([DUMMY_ADAPTER],[no])
 
 AC_ARG_ENABLE([parport],
@@ -367,6 +372,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([linuxspidev],
+  AS_HELP_STRING([--enable-linuxspidev], [Enable building support for SWD via 
Linux SPI DEV]),
+  [build_linuxspidev=$enableval], [build_linuxspidev=no])
+
 AS_CASE([$host_os],
   [linux*], [
     is_linux=yes
@@ -376,6 +385,10 @@ AS_CASE([$host_os],
       AC_MSG_ERROR([sysfsgpio is only available on linux])
     ])
 
+    AS_IF([test "x$build_linuxspidev" = "xyes"], [
+      AC_MSG_ERROR([linuxspidev is only available on linux])
+    ])
+
     AS_CASE([$host_os], [freebsd*], [],
     [
       AS_IF([test "x$build_rshim" = "xyes"], [
@@ -632,6 +645,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_linuxspidev" != "xno"], [
+  AC_DEFINE([BUILD_LINUXSPIDEV], [1], [1 if you want the Linux SPI DEV 
driver.])
+], [
+  AC_DEFINE([BUILD_LINUXSPIDEV], [0], [0 if you don't want the Linux SPI DEV 
driver.])
+])
+
+
 PKG_CHECK_MODULES([LIBUSB1], [libusb-1.0], [
        use_libusb1=yes
        AC_DEFINE([HAVE_LIBUSB1], [1], [Define if you have libusb-1.x])
@@ -727,6 +747,7 @@ PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], 
["x$use_internal_libjaylink" = "xyes" -o
 PROCESS_ADAPTERS([PCIE_ADAPTERS], ["x$is_linux" = "xyes"], [Linux build])
 PROCESS_ADAPTERS([SERIAL_PORT_ADAPTERS], ["x$can_build_buspirate" = "xyes"],
                                          [internal error: validation should 
happen beforehand])
+PROCESS_ADAPTERS([LINUXSPIDEV_ADAPTER], [true], [unused])
 PROCESS_ADAPTERS([DUMMY_ADAPTER], [true], [unused])
 
 AS_IF([test "x$enable_linuxgpiod" != "xno"], [
@@ -782,6 +803,7 @@ AM_CONDITIONAL([AMTJTAGACCEL], [test "x$build_amtjtagaccel" 
= "xyes"])
 AM_CONDITIONAL([GW16012], [test "x$build_gw16012" = "xyes"])
 AM_CONDITIONAL([REMOTE_BITBANG], [test "x$build_remote_bitbang" = "xyes"])
 AM_CONDITIONAL([SYSFSGPIO], [test "x$build_sysfsgpio" = "xyes"])
+AM_CONDITIONAL([LINUXSPIDEV], [test "x$build_linuxspidev" = "xyes"])
 AM_CONDITIONAL([USE_LIBUSB1], [test "x$use_libusb1" = "xyes"])
 AM_CONDITIONAL([IS_CYGWIN], [test "x$is_cygwin" = "xyes"])
 AM_CONDITIONAL([IS_MINGW], [test "x$is_mingw" = "xyes"])
@@ -876,6 +898,7 @@ m4_foreach([adapter], [USB1_ADAPTERS,
        LIBFTDI_USB1_ADAPTERS,
        LIBGPIOD_ADAPTERS,
        LIBJAYLINK_ADAPTERS, PCIE_ADAPTERS, SERIAL_PORT_ADAPTERS,
+  LINUXSPIDEV_ADAPTER,
        DUMMY_ADAPTER,
        OPTIONAL_LIBRARIES,
        COVERAGE],
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 8be834859c..b07f156b04 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -173,6 +173,9 @@ endif
 if SYSFSGPIO
 DRIVERFILES += %D%/sysfsgpio.c
 endif
+if LINUXSPIDEV
+DRIVERFILES += %D%/linuxspidev.c
+endif
 if XLNX_PCIE_XVC
 DRIVERFILES += %D%/xlnx-pcie-xvc.c
 endif
diff --git a/src/jtag/drivers/linuxspidev.c b/src/jtag/drivers/linuxspidev.c
new file mode 100644
index 0000000000..4480929ed5
--- /dev/null
+++ b/src/jtag/drivers/linuxspidev.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Copyright (C) 2020 by Lup Yuen Lee                                    *
+ *   lu...@appkaki.com                                                         
                                           *
+ *                                                                             
                                                                   *
+ *   Copyright (C) 2024 by Richard Pasek                                   *
+ *   rpa...@google.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/>. *
+ ***************************************************************************/
+
+ /**
+  * @file
+  * Implementation of SWD protocol with a Linux SPI device.
+  *
+  * Full duplex SPI transactions are used to read and write from the client at
+  * the same time.
+  *
+  * Electrical connections:
+  * +--------------+                     +--------------+
+  * |              |     1K              |              |
+  * |          MOSI|---/\/\/\---+        |              |
+  * |     Host     |            |        |     Client   |
+  * |          MISO|------------+--------|SWDIO         |
+  * |              |                     |              |
+  * |           SCK|---------------------|SWDCLK        |
+  * |              |                     |              |
+  * +--------------+                     +--------------+
+  *
+  * The 1K resistor works well with most MCUs up to 3 MHz. A lower resistance
+  * could be used to achieve higher speeds granted that the client SWDIO pin 
has
+  * enough drive strength to pull the signal high while being pulled low by 
this
+  * resistor
+  *
+  * openocd.cfg example:
+  * adapter driver linuxspidev
+  *    spidev path "/dev/spidev1.0"
+  *    spidev mode 3
+  */
+
+  // #define LOG_SPI_EXCHANGE  // Uncomment to log SPI exchanges (very verbose)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+#include <jtag/swd.h>
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <helper/time_support.h>
+
+/* Timeout for retrying on SWD WAIT in msec */
+#define SWD_WAIT_TIMEOUT 500
+
+/* LSB to MSB buffer sizes */
+#define MAX_SPI_EXCH_SIZE 32
+
+#define CMD_BITS 8
+#define TURN_BITS 1
+#define ACK_BITS 3
+#define DATA_BITS 32
+#define PARITY_BITS 1
+
+#define SWD_TX_BYTES (DIV_ROUND_UP(CMD_BITS + TURN_BITS + ACK_BITS + TURN_BITS 
+ DATA_BITS + PARITY_BITS, 8))
+#define SWD_RX_BYTES (DIV_ROUND_UP(CMD_BITS + TURN_BITS + ACK_BITS + DATA_BITS 
+ PARITY_BITS + TURN_BITS, 8))
+
+/// File descriptor for SPI device
+static int spi_fd = -1;
+
+static int queued_retval;
+
+static int spidev_swd_switch_seq(enum swd_special_seq seq);
+static void spidev_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t 
ap_delay_clk);
+
+// SPI Configuration
+static char *spi_path;
+static uint32_t spi_mode = SPI_MODE_3; // Note: SPI in LSB mode is not often 
supported. We'll flip LSB to MSB ourselves.
+static uint32_t speed_khz = 3000;
+static const uint8_t spi_bits = 8;
+static const uint16_t delay = 0;
+
+static void spi_exchange(const uint8_t *txbuf, uint8_t *rxbuf, unsigned int 
len)
+{
+       static uint8_t tx_buf[MAX_SPI_EXCH_SIZE];
+       static uint8_t rx_buf[MAX_SPI_EXCH_SIZE];
+
+#ifdef LOG_SPI_EXCHANGE
+       LOG_OUTPUT("spi_exchange: len=%d", len);
+#endif // LOG_SPI_EXCHANGE
+
+       if (len == 0 || (!txbuf && !rxbuf)) {
+               LOG_DEBUG("exchange with no length len=%d ", len);
+               return;
+       }
+
+       if (len >= MAX_SPI_EXCH_SIZE) {
+               LOG_ERROR("exchange too large len=%d ", len);
+               return;
+       }
+
+       if (txbuf) {
+               // Reverse LSB to MSB
+               for (unsigned int i = 0; i < len; i++) {
+                       tx_buf[i] = flip_u32(txbuf[i], 8);
+               }
+#ifdef LOG_SPI_EXCHANGE
+               if (len != 0) {
+                       LOG_OUTPUT(" tx data=");
+                       for (unsigned int i = 0; i < len; i++) {
+                               LOG_OUTPUT("%.2X ", tx_buf[i]);
+                       }
+               }
+               LOG_OUTPUT("\n");
+
+#endif // LOG_SPI_EXCHANGE
+       }
+       // Transmit the MSB buffer to SPI device.
+       struct spi_ioc_transfer tr = {
+               .tx_buf = (__u64)(txbuf ? tx_buf : NULL),
+               .rx_buf = (__u64)(rxbuf ? rx_buf : NULL),
+               .len = len,
+               .delay_usecs = delay,
+               .speed_hz = speed_khz * 1000,
+               .bits_per_word = spi_bits,
+       };
+       int ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
+
+       // Check SPI result.
+       if (ret < 1) {
+               LOG_ERROR("exchange failed");
+               return;
+       }
+
+       if (rxbuf) {
+               // Reverse MSB to LSB
+               for (unsigned int i = 0; i < len; i++) {
+                       rxbuf[i] = flip_u32(rx_buf[i], 8);
+               }
+#ifdef LOG_SPI_EXCHANGE
+               if (len != 0) {
+                       LOG_OUTPUT(" rx data=");
+               }
+               for (unsigned int i = 0; i < len; i++) {
+                       LOG_OUTPUT("%.2X ", rxbuf[i]);
+               }
+               puts("");
+
+#endif // LOG_SPI_EXCHANGE
+       }
+}
+
+static void spi_delay(unsigned int cycles)
+{
+       const uint8_t zero = 0;
+
+       for (unsigned int i = 0; i < DIV_ROUND_UP(cycles, 8); i++) {
+               spi_exchange(&zero, NULL, sizeof(zero));
+       }
+}
+
+/// Set JTAG speed (not used)
+static int spidev_khz(int khz, int *jtag_speed)
+{
+       // TODO
+       if (!khz) {
+               LOG_DEBUG("RCLK not supported");
+               return ERROR_FAIL;
+       }
+       *jtag_speed = 0;
+       return ERROR_OK;
+}
+
+/// Set speed div
+static int spidev_speed_div(int speed, int *khz)
+{
+       *khz = speed_khz;
+       return ERROR_OK;
+}
+
+/// Set JTAG delay (not used)
+static int spidev_speed(int speed)
+{
+       return ERROR_OK;
+}
+
+/// Is SWD transport supported
+static bool spidev_swd_mode_possible(void)
+{
+       return 1;
+}
+
+/// Init driver
+static int spidev_init(void)
+{
+       LOG_INFO("SPI SWD driver");
+
+       if (spidev_swd_mode_possible()) {
+               LOG_INFO("SWD only mode enabled");
+       } else {
+               LOG_ERROR("Mode not supported");
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       if (spi_fd >= 0)
+               return ERROR_OK;
+
+       // Open SPI device.
+       spi_fd = open(spi_path, O_RDWR);
+       if (spi_fd < 0) {
+               LOG_ERROR("Failed to open SPI port at %s", spi_path);
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       // Set SPI mode.
+       int ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode);
+       if (ret == -1) {
+               LOG_ERROR("Failed to set SPI mode %d", spi_mode);
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       // Set SPI bits per word.
+       ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);
+       if (ret == -1) {
+               LOG_ERROR("Failed to set SPI %d bits per transfer", spi_bits);
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       // Set SPI read and write max speed.
+       uint32_t speed = speed_khz * 1000;
+       ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
+       if (ret == -1) {
+               LOG_ERROR("Failed to set SPI %u max speed", speed);
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       spidev_swd_switch_seq(JTAG_TO_SWD);
+
+       LOG_INFO("Opened SPI device at %s in mode %d with %d bits per transfer 
at %u kHz", spi_path, spi_mode, spi_bits, speed_khz);
+
+       return ERROR_OK;
+}
+
+/// Terminate driver
+static int spidev_quit(void)
+{
+       free(spi_path);
+
+       if (spi_fd < 0)
+               return ERROR_OK;
+
+       close(spi_fd);
+       spi_fd = -1;
+       return ERROR_OK;
+}
+
+static void swd_clear_sticky_errors(void)
+{
+       spidev_swd_write_reg(swd_cmd(false, false, DP_ABORT),
+               STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
+}
+
+static int spidev_swd_init(void)
+{
+       LOG_DEBUG("spidev_swd_init");
+       return ERROR_OK;
+}
+
+static int spidev_swd_run_queue(void)
+{
+       /* A transaction must be followed by another transaction or at least 8 
idle cycles to
+        * ensure that data is clocked through the AP. */
+       spi_delay(8);
+
+       int retval = queued_retval;
+       queued_retval = ERROR_OK;
+       LOG_DEBUG_IO("SWD queue return value: %02x", retval);
+       return retval;
+}
+
+static void spidev_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t 
ap_delay_clk)
+{
+       assert(cmd & SWD_CMD_RNW);
+
+       if (queued_retval != ERROR_OK) {
+               LOG_DEBUG("Skip spidev_swd_read_reg because queued_retval=%d", 
queued_retval);
+               return;
+       }
+
+       int64_t timeout = timeval_ms() + SWD_WAIT_TIMEOUT;
+       for (unsigned int retry = 0;; retry++) {
+               uint8_t cmd_trn_ack_data_parity_trn_tx[SWD_RX_BYTES] = { 0 };
+               uint8_t cmd_trn_ack_data_parity_trn_rx[SWD_RX_BYTES] = { 0 };
+
+               cmd |= SWD_CMD_START | SWD_CMD_PARK;
+               buf_set_u32(cmd_trn_ack_data_parity_trn_tx, 0, CMD_BITS, cmd);
+
+               spi_exchange(cmd_trn_ack_data_parity_trn_tx, 
cmd_trn_ack_data_parity_trn_rx, SWD_RX_BYTES);
+
+               int ack = buf_get_u32(cmd_trn_ack_data_parity_trn_rx, CMD_BITS 
+ TURN_BITS, ACK_BITS);
+               uint32_t data = buf_get_u32(cmd_trn_ack_data_parity_trn_rx, 
CMD_BITS + TURN_BITS + ACK_BITS, DATA_BITS);
+               int parity = buf_get_u32(cmd_trn_ack_data_parity_trn_rx, 
CMD_BITS + TURN_BITS + ACK_BITS + DATA_BITS, PARITY_BITS);
+
+               LOG_CUSTOM_LEVEL((ack != SWD_ACK_OK && (retry == 0 || ack != 
SWD_ACK_WAIT))
+                       ? LOG_LVL_DEBUG
+                       : LOG_LVL_DEBUG_IO,
+                       "%s %s read reg %X = %08" PRIx32,
+                       ack == SWD_ACK_OK ? "OK" :
+                       ack == SWD_ACK_WAIT ? "WAIT" :
+                       ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+                       cmd & SWD_CMD_APNDP ? "AP" : "DP",
+                       (cmd & SWD_CMD_A32) >> 1,
+                       data);
+
+               if (ack == SWD_ACK_WAIT && timeval_ms() <= timeout) {
+                       swd_clear_sticky_errors();
+                       if (retry > 20)
+                               alive_sleep(1);
+
+                       continue;
+               }
+
+               if (retry > 1)
+                       LOG_DEBUG("SWD WAIT: retried %u times", retry);
+
+               if (ack != SWD_ACK_OK) {
+                       queued_retval = swd_ack_to_error_code(ack);
+                       return;
+               }
+
+               if (parity != parity_u32(data)) {
+                       LOG_ERROR("Wrong parity detected");
+                       queued_retval = ERROR_FAIL;
+                       return;
+               }
+
+               if (value)
+                       *value = data;
+
+               if (cmd & SWD_CMD_APNDP)
+                       spi_delay(ap_delay_clk);
+               return;
+       }
+}
+
+static void spidev_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t 
ap_delay_clk)
+{
+       assert(!(cmd & SWD_CMD_RNW));
+
+       if (queued_retval != ERROR_OK) {
+               LOG_DEBUG("Skip spidev_swd_write_reg because queued_retval=%d", 
queued_retval);
+               return;
+       }
+
+       int64_t timeout = timeval_ms() + SWD_WAIT_TIMEOUT;
+
+       /* Devices do not reply to DP_TARGETSEL write cmd, ignore received ack 
*/
+       bool check_ack = swd_cmd_returns_ack(cmd);
+
+       /* init the array to silence scan-build */
+       uint8_t cmd_trn_ack_data_parity_trn_tx[SWD_TX_BYTES] = { 0 };
+       uint8_t cmd_trn_ack_data_parity_trn_rx[SWD_TX_BYTES] = { 0 };
+       for (unsigned int retry = 0;; retry++) {
+
+               cmd |= SWD_CMD_START | SWD_CMD_PARK;
+
+               buf_set_u32(cmd_trn_ack_data_parity_trn_tx, 0, CMD_BITS, cmd);
+               buf_set_u32(cmd_trn_ack_data_parity_trn_tx, CMD_BITS + 
TURN_BITS + ACK_BITS + TURN_BITS, DATA_BITS, value);
+               buf_set_u32(cmd_trn_ack_data_parity_trn_tx, CMD_BITS + 
TURN_BITS + ACK_BITS + TURN_BITS + DATA_BITS, PARITY_BITS, parity_u32(value));
+
+               spi_exchange(cmd_trn_ack_data_parity_trn_tx, 
cmd_trn_ack_data_parity_trn_rx, SWD_TX_BYTES);
+
+               int ack = buf_get_u32(cmd_trn_ack_data_parity_trn_rx, CMD_BITS 
+ TURN_BITS, ACK_BITS);
+               LOG_CUSTOM_LEVEL((check_ack && ack != SWD_ACK_OK && (retry == 0 
|| ack != SWD_ACK_WAIT))
+                       ? LOG_LVL_DEBUG
+                       : LOG_LVL_DEBUG_IO,
+                       "%s%s %s write reg %X = %08" PRIx32,
+                       check_ack ? "" : "ack ignored ",
+                       ack == SWD_ACK_OK ? "OK" :
+                       ack == SWD_ACK_WAIT ? "WAIT" :
+                       ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+                       cmd & SWD_CMD_APNDP ? "AP" : "DP",
+                       (cmd & SWD_CMD_A32) >> 1,
+                       buf_get_u32(cmd_trn_ack_data_parity_trn_tx, CMD_BITS + 
TURN_BITS + ACK_BITS + TURN_BITS, DATA_BITS));
+
+               if (check_ack && ack == SWD_ACK_WAIT && timeval_ms() <= 
timeout) {
+                       swd_clear_sticky_errors();
+                       if (retry > 20)
+                               alive_sleep(1);
+
+                       continue;
+               }
+
+               if (retry > 1)
+                       LOG_DEBUG("SWD WAIT: retried %u times", retry);
+
+               if (check_ack && ack != SWD_ACK_OK) {
+                       queued_retval = swd_ack_to_error_code(ack);
+                       return;
+               }
+
+               if (cmd & SWD_CMD_APNDP)
+                       spi_delay(ap_delay_clk);
+
+               return;
+       }
+}
+
+static int spidev_swd_switch_seq(enum swd_special_seq seq)
+{
+       switch (seq) {
+       case LINE_RESET:
+               LOG_DEBUG_IO("SWD line reset");
+               spi_exchange(swd_seq_line_reset, NULL, swd_seq_line_reset_len / 
8);
+               break;
+       case JTAG_TO_SWD:
+               LOG_DEBUG("JTAG-to-SWD");
+               spi_exchange(swd_seq_jtag_to_swd, NULL, swd_seq_jtag_to_swd_len 
/ 8);
+               break;
+       case JTAG_TO_DORMANT:
+               LOG_DEBUG("JTAG-to-DORMANT");
+               spi_exchange(swd_seq_jtag_to_dormant, NULL, 
swd_seq_jtag_to_dormant_len / 8);
+               break;
+       case SWD_TO_JTAG:
+               LOG_DEBUG("SWD-to-JTAG");
+               spi_exchange(swd_seq_swd_to_jtag, NULL, swd_seq_swd_to_jtag_len 
/ 8);
+               break;
+       case SWD_TO_DORMANT:
+               LOG_DEBUG("SWD-to-DORMANT");
+               spi_exchange(swd_seq_swd_to_dormant, NULL, 
swd_seq_swd_to_dormant_len / 8);
+               break;
+       case DORMANT_TO_SWD:
+               LOG_DEBUG("DORMANT-to-SWD");
+               spi_exchange(swd_seq_dormant_to_swd, NULL, 
swd_seq_dormant_to_swd_len / 8);
+               break;
+       case DORMANT_TO_JTAG:
+               LOG_DEBUG("DORMANT-to-JTAG");
+               spi_exchange(swd_seq_dormant_to_jtag, NULL, 
swd_seq_dormant_to_jtag_len / 8);
+               break;
+       default:
+               LOG_ERROR("Sequence %d not supported", seq);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(spidev_handle_path_command)
+{
+       if (CMD_ARGC == 1) {
+               free(spi_path);
+               spi_path = strdup(CMD_ARGV[0]);
+       } else {
+               LOG_ERROR("expected exactly one argument to spidev path 
<path>");
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(spidev_handle_mode_command)
+{
+       if (CMD_ARGC == 1)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], spi_mode);
+       else
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       return ERROR_OK;
+}
+
+const struct swd_driver spidev_swd = {
+       .init = spidev_swd_init,
+       .switch_seq = spidev_swd_switch_seq,
+       .read_reg = spidev_swd_read_reg,
+       .write_reg = spidev_swd_write_reg,
+       .run = spidev_swd_run_queue,
+};
+
+
+static const struct command_registration spidev_subcommand_handlers[] = {
+       {
+               .name = "path",
+               .handler = &spidev_handle_path_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the path to the spidev device",
+               .usage = "example: /dev/spidev0.0",
+       },
+       {
+               .name = "mode",
+               .handler = &spidev_handle_mode_command,
+               .mode = COMMAND_CONFIG,
+               .help = "set the mode of the spi port",
+               .usage = "(number in decimal)",
+       },
+};
+
+static const struct command_registration spidev_command_handlers[] = {
+       {
+               .name = "spidev",
+               .mode = COMMAND_ANY,
+               .help = "perform spidev management",
+               .chain = spidev_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+/// Only SWD transport supported
+static const char *const spidev_transports[] = { "swd", NULL };
+
+struct adapter_driver linuxspidev_adapter_driver = {
+       .name = "linuxspidev",
+       .transports = spidev_transports,
+       .commands = spidev_command_handlers,
+
+       .init = spidev_init,
+       .quit = spidev_quit,
+       .speed = spidev_speed,
+       .khz = spidev_khz,
+       .speed_div = spidev_speed_div,
+
+       .swd_ops = &spidev_swd,
+};
diff --git a/src/jtag/interface.h b/src/jtag/interface.h
index b448851dc4..f69326492a 100644
--- a/src/jtag/interface.h
+++ b/src/jtag/interface.h
@@ -386,6 +386,7 @@ extern struct adapter_driver jtag_dpi_adapter_driver;
 extern struct adapter_driver jtag_vpi_adapter_driver;
 extern struct adapter_driver kitprog_adapter_driver;
 extern struct adapter_driver linuxgpiod_adapter_driver;
+extern struct adapter_driver linuxspidev_adapter_driver;
 extern struct adapter_driver opendous_adapter_driver;
 extern struct adapter_driver openjtag_adapter_driver;
 extern struct adapter_driver osbdm_adapter_driver;
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 67f0838e39..e49bd9e0f3 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -123,6 +123,9 @@ struct adapter_driver *adapter_drivers[] = {
 #if BUILD_LINUXGPIOD == 1
                &linuxgpiod_adapter_driver,
 #endif
+#if BUILD_LINUXSPIDEV == 1
+               &linuxspidev_adapter_driver,
+#endif
 #if BUILD_XLNX_PCIE_XVC == 1
                &xlnx_pcie_xvc_adapter_driver,
 #endif

-- 

Reply via email to