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 luppy@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.

TEST:
  Connects successfully with the following combinations:
  Hosts:
    Raspberry Pi 4B
    Unnamed Qualcomm SoC with QUPv3 based SPI port
  Targets:
    Raspberry Pi 2040
    Nordic nRF52840
    NXP RT500

Change-Id: Ic2f38a1806085d527e6f999a3d15aea6f32d1019
Signed-off-by: Richard Pasek <rpasek@google.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8645
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: zapb <dev@zapb.de>
Tested-by: jenkins
This commit is contained in:
Richard Pasek
2024-12-11 00:43:57 -05:00
committed by Tomas Vanek
parent 26f2df80c3
commit 83e0293f7b
7 changed files with 711 additions and 0 deletions

View File

@@ -163,6 +163,9 @@ m4_define([PCIE_ADAPTERS],
m4_define([SERIAL_PORT_ADAPTERS],
[[[buspirate], [Bus Pirate], [BUS_PIRATE]]])
m4_define([LINUXSPIDEV_ADAPTER],
[[[linuxspidev], [Linux spidev 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],
@@ -289,6 +292,7 @@ AC_ARG_ADAPTERS([
LIBFTDI_ADAPTERS,
LIBFTDI_USB1_ADAPTERS,
LIBGPIOD_ADAPTERS,
LINUXSPIDEV_ADAPTER,
SERIAL_PORT_ADAPTERS,
DUMMY_ADAPTER,
PCIE_ADAPTERS,
@@ -726,6 +730,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], ["x$is_linux" = "xyes"], [Linux spidev])
PROCESS_ADAPTERS([DUMMY_ADAPTER], [true], [unused])
AS_IF([test "x$enable_linuxgpiod" != "xno"], [
@@ -875,6 +880,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],

View File

@@ -614,6 +614,9 @@ emulation model of target hardware.
@item @b{xlnx_pcie_xvc}
@* A JTAG driver exposing Xilinx Virtual Cable over PCI Express to OpenOCD as JTAG/SWD interface.
@item @b{linuxspidev}
@* A SPI based SWD driver using Linux SPI devices.
@item @b{linuxgpiod}
@* A bitbang JTAG driver using Linux GPIO through library libgpiod.
@@ -3401,6 +3404,70 @@ See @file{interface/beaglebone-swd-native.cfg} for a sample configuration file.
@end deffn
@deffn {Interface Driver} {linuxspidev}
Linux provides userspace access to SPI through spidev. Full duplex SPI
transactions are used to simultaneously read and write to/from the target to
emulate the SWD transport.
@deffn {Config Command} {spidev path} path
Specifies the path to the spidev device.
@end deffn
@deffn {Config Command} {spidev mode} value
Set the mode of the spi port with optional bit flags (default=3).
See /usr/include/linux/spi/spidev.h for all of the SPI mode options.
@end deffn
@deffn {Config Command} {spidev queue_entries} value
Set the maximum number of queued transactions per spi exchange (default=64).
More queued transactions may offer greater performance when the target doesn't
need to wait. On the contrary higher numbers will reduce performance when the
target requests a wait as all queued transactions will need to be exchanged
before spidev can see the wait request.
@end deffn
See @file{tcl/interface/spidev_example.cfg} for a sample configuration file.
Electrical connections:
@example
+--------------+ +--------------+
| | 1K | |
| MOSI|---/\/\/\---+ | |
| Host | | | Target |
| MISO|------------+---|SWDIO |
| | | |
| SCK|----------------|SWDCLK |
| | | |
+--------------+ +--------------+
@end example
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 target SWDIO pin has
enough drive strength to pull the signal high while being pulled low by this
resistor.
If you are having trouble here are some tips:
@itemize @bullet
@item @b{Make sure MISO and MOSI are tied together with a 1K resistor.}
MISO should be attached to the target.
@item @b{Make sure that your host and target are using the same I/O voltage}
(for example both are using 3.3 volts).
@item @b{Your host's SPI port may not idle low.}
This will lead to an additional clock edge being sent to the target, causing
the host and target being 1 clock off from each other. Try setting
SPI_MOSI_IDLE_LOW in spi_mode. Try using a different spi_mode (0 - 3).
@item @b{Your target may pull SWDIO and/or SWDCLK high.}
This will create an extra edge when the host releases control of the SPI port
at the end of a transaction. You'll need to confirm this with a scope or meter.
Try installing 10K resistors on SWDIO and SWDCLK to ground to stop this.
@end itemize
@end deffn
@deffn {Interface Driver} {linuxgpiod}
Linux provides userspace access to GPIO through libgpiod since Linux kernel

View File

@@ -176,6 +176,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

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Example config for using Linux spidev as a SWD adapter.
adapter driver linuxspidev
adapter speed 3000
spidev path "/dev/spidev0.0"
spidev mode 3
spidev queue_entries 64