From b394664dd9ffb0a9580f8d6bb44731a28ec764d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nerijus=20Bend=C5=BEi=C5=ABnas?= Date: Sun, 7 Apr 2024 09:35:28 +0300 Subject: [PATCH] spi: shell: add simple SPI shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspired by I2C shell. Useful during SPI device driver development or for debugging. Usage example (read JEDEC ID): ``` uart:~$ spi conf spi4 1000000 oh uart:~$ spi transceive 9f 00 00 00 TX: 00000000: 9f 00 00 00 |.... | RX: 00000000: 00 ef 40 19 |.... | ``` Signed-off-by: Nerijus Bendžiūnas --- drivers/spi/CMakeLists.txt | 1 + drivers/spi/Kconfig | 9 +++ drivers/spi/spi_shell.c | 155 +++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/spi/spi_shell.c diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 8bbae49d1c2..840c9dcdc5f 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/spi.h) zephyr_library() +zephyr_library_sources_ifdef(CONFIG_SPI_SHELL spi_shell.c) zephyr_library_sources_ifdef(CONFIG_SPI_TELINK_B91 spi_b91.c) zephyr_library_sources_ifdef(CONFIG_SPI_CC13XX_CC26XX spi_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c7e40016b4e..77f0a081661 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -13,6 +13,15 @@ menuconfig SPI if SPI +config SPI_SHELL + bool "SPI Shell" + depends on SHELL + help + Enable SPI Shell. + + The currently SPI shell supports simple SPI write/read (transceive) + operation. + config SPI_ASYNC bool "Asynchronous call support" select POLL diff --git a/drivers/spi/spi_shell.c b/drivers/spi/spi_shell.c new file mode 100644 index 00000000000..aa3d5b2fea5 --- /dev/null +++ b/drivers/spi/spi_shell.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024 Astroligt + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TXRX_ARGV_BYTES (1) +#define CONF_ARGV_DEV (1) +#define CONF_ARGV_FREQUENCY (2) +#define CONF_ARGV_SETTINGS (3) + +/* Maximum bytes we can write and read at once */ +#define MAX_SPI_BYTES MIN((CONFIG_SHELL_ARGC_MAX - TXRX_ARGV_BYTES), 32) + +static struct device *spi_device; +static struct spi_config config = {.frequency = 1000000, + .operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)}; + +static void device_name_get(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, "spi"); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); + +static int cmd_spi_transceive(const struct shell *ctx, size_t argc, char **argv) +{ + uint8_t rx_buffer[MAX_SPI_BYTES] = {0}; + uint8_t tx_buffer[MAX_SPI_BYTES] = {0}; + + if (spi_device == NULL) { + shell_error(ctx, "SPI device isn't configured. Use `spi conf`"); + return -ENODEV; + } + + int bytes_to_send = argc - TXRX_ARGV_BYTES; + + for (int i = 0; i < bytes_to_send; i++) { + tx_buffer[i] = strtol(argv[TXRX_ARGV_BYTES + i], NULL, 16); + } + + const struct spi_buf tx_buffers = {.buf = tx_buffer, .len = bytes_to_send}; + const struct spi_buf rx_buffers = {.buf = rx_buffer, .len = bytes_to_send}; + + const struct spi_buf_set tx_buf_set = {.buffers = &tx_buffers, .count = 1}; + const struct spi_buf_set rx_buf_set = {.buffers = &rx_buffers, .count = 1}; + + int ret = spi_transceive(spi_device, &config, &tx_buf_set, &rx_buf_set); + + if (ret < 0) { + shell_error(ctx, "spi_transceive returned %d", ret); + return ret; + } + + shell_print(ctx, "TX:"); + shell_hexdump(ctx, tx_buffer, bytes_to_send); + + shell_print(ctx, "RX:"); + shell_hexdump(ctx, rx_buffer, bytes_to_send); + + return ret; +} + +static int cmd_spi_conf(const struct shell *ctx, size_t argc, char **argv) +{ + spi_operation_t operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER; + + /* warning: initialization discards 'const' qualifier from pointer */ + /* target type */ + struct device *dev = (struct device *)device_get_binding(argv[CONF_ARGV_DEV]); + + if (dev == NULL) { + shell_error(ctx, "device %s not found.", argv[CONF_ARGV_DEV]); + return -ENODEV; + } + + uint32_t frequency = strtol(argv[CONF_ARGV_FREQUENCY], NULL, 10); + + if (!IN_RANGE(frequency, 100 * 1000, 80 * 1000 * 1000)) { + shell_error(ctx, "frequency must be between 100000 and 80000000"); + return -EINVAL; + } + + /* no settings */ + if (argc == (CONF_ARGV_FREQUENCY + 1)) { + goto out; + } + + char *opts = argv[CONF_ARGV_SETTINGS]; + bool all_opts_is_valid = true; + + while (*opts != '\0') { + switch (*opts) { + case 'o': + operation |= SPI_MODE_CPOL; + break; + case 'h': + operation |= SPI_MODE_CPHA; + break; + case 'l': + operation |= SPI_TRANSFER_LSB; + break; + case 'T': + operation |= SPI_FRAME_FORMAT_TI; + break; + default: + all_opts_is_valid = false; + shell_error(ctx, "invalid setting %c", *opts); + } + opts++; + } + + if (!all_opts_is_valid) { + return -EINVAL; + } + +out: + config.frequency = frequency; + config.operation = operation; + spi_device = dev; + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_spi_cmds, + SHELL_CMD_ARG(conf, &dsub_device_name, + "Configure SPI\n" + "Usage: spi conf []\n" + " - any sequence of letters:" + "o - SPI_MODE_CPOL\n" + "h - SPI_MODE_CPHA\n" + "l - SPI_TRANSFER_LSB\n" + "T - SPI_FRAME_FORMAT_TI\n" + "example: spi conf spi1 1000000 ol", + cmd_spi_conf, 3, 1), + SHELL_CMD_ARG(transceive, NULL, + "Transceive data to and from an SPI device\n" + "Usage: spi transceive [ ...]", + cmd_spi_transceive, 2, MAX_SPI_BYTES - 1), + SHELL_SUBCMD_SET_END); + +SHELL_CMD_REGISTER(spi, &sub_spi_cmds, "SPI commands", NULL);