From 624e6dbedc59b3b6d869cd54ca6f8e3997391d41 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Wed, 12 Aug 2020 01:41:06 +0200 Subject: Add initial SPI support Signed-off-by: Marc Schink --- libjaylink/Makefile.am | 1 + libjaylink/libjaylink.h | 28 ++++++++- libjaylink/spi.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ libjaylink/strutil.c | 2 + libjaylink/target.c | 1 + 5 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 libjaylink/spi.c diff --git a/libjaylink/Makefile.am b/libjaylink/Makefile.am index 62c5489..8e10212 100644 --- a/libjaylink/Makefile.am +++ b/libjaylink/Makefile.am @@ -40,6 +40,7 @@ libjaylink_la_SOURCES = \ list.c \ log.c \ socket.c \ + spi.c \ strutil.c \ swd.c \ swo.c \ diff --git a/libjaylink/libjaylink.h b/libjaylink/libjaylink.h index 8f48c3d..8798e42 100644 --- a/libjaylink/libjaylink.h +++ b/libjaylink/libjaylink.h @@ -156,7 +156,9 @@ enum jaylink_device_capability { /** Device supports EMUCOM. */ JAYLINK_DEV_CAP_EMUCOM = 33, /** Device supports ethernet connectivity. */ - JAYLINK_DEV_CAP_ETHERNET = 38 + JAYLINK_DEV_CAP_ETHERNET = 38, + /** Device supports SPI. */ + JAYLINK_DEV_CAP_SPI = 56, }; /** Hardware information. */ @@ -240,6 +242,8 @@ enum jaylink_target_interface { JAYLINK_TIF_FINE = 3, /** 2-wire JTAG for PIC32 compliant devices. */ JAYLINK_TIF_2W_JTAG_PIC32 = 4, + /** Serial Peripheral Interface (SPI). */ + JAYLINK_TIF_SPI = 5, /** Compact JTAG (cJTAG). **/ JAYLINK_TIF_CJTAG = 7, }; @@ -268,6 +272,22 @@ enum jaylink_swo_mode { JAYLINK_SWO_MODE_UART = 0 }; +/** Serial Peripheral Interface (SPI) flags. */ +enum jaylink_spi_flag { + /** Do not drive chip select (CS) before the transfer begins. */ + JAYLINK_SPI_FLAG_CS_START_U = 0x00, + /** Drive chip select (CS) low before the transfer begins. */ + JAYLINK_SPI_FLAG_CS_START_0 = 0x02, + /** Drive chip select (CS) high before the transfer begins. */ + JAYLINK_SPI_FLAG_CS_START_1 = 0x03, + /** Do not drive chip select (CS) after the transfer is complete. */ + JAYLINK_SPI_FLAG_CS_END_U = 0x00, + /** Drive chip select (CS) low after the transfer is complete. */ + JAYLINK_SPI_FLAG_CS_END_0 = 0x08, + /** Drive chip select (CS) high after the transfer is complete. */ + JAYLINK_SPI_FLAG_CS_END_1 = 0x0c, +}; + /** Target interface speed information. */ struct jaylink_speed { /** Base frequency in Hz. */ @@ -569,6 +589,12 @@ JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx, JAYLINK_API const char *jaylink_log_get_domain( const struct jaylink_context *ctx); +/*--- spi.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_spi_io(struct jaylink_device_handle *devh, + const uint8_t *mosi, uint8_t *miso, uint32_t length, + uint32_t flags); + /*--- strutil.c -------------------------------------------------------------*/ JAYLINK_API int jaylink_parse_serial_number(const char *str, diff --git a/libjaylink/spi.c b/libjaylink/spi.c new file mode 100644 index 0000000..4f8ab04 --- /dev/null +++ b/libjaylink/spi.c @@ -0,0 +1,148 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2016-2020 Marc Schink + * + * 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 . + */ + +#include + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Serial Peripheral Interface (SPI) functions. + */ + +/** @cond PRIVATE */ +#define CMD_SPI 0x15 + +#define SPI_CMD_IO 0x01 +/** @endcond */ + +/** + * Perform SPI I/O operation. + * + * The device acts as master and operates in mode 3 (CPOL = 1, CPHA = 1). Data + * is transferred with the most significant bit (MSB) first. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SPI capability and if the #JAYLINK_TIF_SPI interface + * is available and selected. + * + * @param[in,out] devh Device handle. + * @param[in] mosi Buffer to read MOSI data from. Can be NULL. + * @param[out] miso Buffer to store MISO data on success. Its content + * is undefined on failure. The buffer must be large enough to + * contain at least the specified number of bytes to transfer. + * Can be NULL. + * @param[in] length Number of bytes to transfer. + * @param[in] flags Flags, see #jaylink_spi_flag for more details. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.3.0 + */ +JAYLINK_API int jaylink_spi_io(struct jaylink_device_handle *devh, + const uint8_t *mosi, uint8_t *miso, uint32_t length, + uint32_t flags) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[20]; + uint32_t mosi_length; + uint32_t miso_length; + uint32_t num_transferred_bytes; + + if (!devh || !length) + return JAYLINK_ERR_ARG; + + if (!mosi && !miso) + return JAYLINK_ERR_ARG; + + mosi_length = (mosi) ? length : 0; + miso_length = (miso) ? length : 0; + ctx = devh->dev->ctx; + + buf[0] = CMD_SPI; + buf[1] = SPI_CMD_IO; + buf[2] = 0x00; + buf[3] = 0x00; + + buffer_set_u32(buf, mosi_length + 8, 4); + buffer_set_u32(buf, miso_length + 4, 8); + buffer_set_u32(buf, length * 8, 12); + buffer_set_u32(buf, flags, 16); + + ret = transport_start_write_read(devh, 20 + mosi_length, + miso_length + 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buf, 20); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s", + jaylink_strerror(ret)); + return ret; + } + + if (mosi) { + ret = transport_write(devh, mosi, mosi_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s", + jaylink_strerror(ret)); + return ret; + } + } + + if (miso) { + ret = transport_read(devh, miso, miso_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s", + jaylink_strerror(ret)); + return ret; + } + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s", + jaylink_strerror(ret)); + return ret; + } + + num_transferred_bytes = buffer_get_u32(buf, 0); + + if (num_transferred_bytes != length) { + log_err(ctx, "Unexpected number of transferred bytes"); + return JAYLINK_ERR_PROTO; + } + + return JAYLINK_OK; +} diff --git a/libjaylink/strutil.c b/libjaylink/strutil.c index 3658683..0f344c1 100644 --- a/libjaylink/strutil.c +++ b/libjaylink/strutil.c @@ -116,6 +116,8 @@ JAYLINK_API const char *jaylink_target_interface_string( return "FINE"; case JAYLINK_TIF_2W_JTAG_PIC32: return "2-wire JTAG for PIC32"; + case JAYLINK_TIF_SPI: + return "SPI"; case JAYLINK_TIF_CJTAG: return "cJTAG"; default: diff --git a/libjaylink/target.c b/libjaylink/target.c index 8df96af..c51ebcd 100644 --- a/libjaylink/target.c +++ b/libjaylink/target.c @@ -217,6 +217,7 @@ JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh, case JAYLINK_TIF_BDM3: case JAYLINK_TIF_FINE: case JAYLINK_TIF_2W_JTAG_PIC32: + case JAYLINK_TIF_SPI: case JAYLINK_TIF_CJTAG: break; default: -- cgit v1.1