[PATCH 1/2] spi: Add the SPI daisy chain support.
From: Adrian Fiergolski
Date: Fri Jul 03 2020 - 10:13:38 EST
The implementation is transparent for the SPI devices and doesn't require
their modifications. It is based on a virtual SPI device (spi-daisy_chain)
and defines two required device tree properties ('spi-daisy-chain-len' and
'spi-daisy-chain-noop') and one optional
('spi-daisy-chain-bits_per_word'). It has been tested on hardware with a
chain of three ltc2694 devices (kernel v4.19).
Signed-off-by: Adrian Fiergolski <adrian.fiergolski@xxxxxxxxxxxxx>
---
drivers/spi/Kconfig | 8 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-daisy_chain.c | 428 ++++++++++++++++++++++++++++
drivers/spi/spi.c | 65 ++++-
include/linux/spi/spi-daisy_chain.h | 32 +++
include/linux/spi/spi.h | 17 +-
6 files changed, 541 insertions(+), 10 deletions(-)
create mode 100644 drivers/spi/spi-daisy_chain.c
create mode 100644 include/linux/spi/spi-daisy_chain.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8f1f8fca79e3..822c4b4bdd5c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -55,6 +55,14 @@ config SPI_MEM
This extension is meant to simplify interaction with SPI memories
by providing a high-level interface to send memory-like commands.
+config SPI_DAISY_CHAIN
+ bool "SPI daisy chain support"
+ depends on OF
+ help
+ This enables support for the SPI daisy chains.
+ This extension provides a virtual SPI daisy chain device which
+ links together physical SPI devices on a common SPI daisy chain.
+
comment "SPI Master Controller Drivers"
config SPI_ALTERA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d2e41d3d464a..dbcb98480f51 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_MEM) += spi-mem.o
+obj-$(CONFIG_SPI_DAISY_CHAIN) += spi-daisy_chain.o
obj-$(CONFIG_SPI_MUX) += spi-mux.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
diff --git a/drivers/spi/spi-daisy_chain.c b/drivers/spi/spi-daisy_chain.c
new file mode 100644
index 000000000000..37870fe71028
--- /dev/null
+++ b/drivers/spi/spi-daisy_chain.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * A driver handling the SPI daisy chaines.
+ *
+ * Copyright (C) 2020 Fastree3D
+ * Adrian Fiergolski <Adrian.Fiergolski@xxxxxxxxxxxxx>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+#include <linux/spi/spi-daisy_chain.h>
+
+/**
+ * spi_daisy_chain_message_pre - generate SPI daisy chain message
+ * @spi: device with which data will be exchanged
+ * @message: describes the data transfers
+ *
+ * The call checks if the SPI device is part of daisy chain. If so, it
+ * generates a message containg no-operation codes
+ * of unaddressed device of a given daisy chain.
+ **/
+
+int spi_daisy_chain_message_pre(struct spi_device *spi,
+ struct spi_message *message)
+{
+ struct spi_transfer *tr, *ntr;
+ struct spi_daisy_chain_device *spi_chain_dev;
+ int rc;
+
+ //the device is not part of a daisy-chain
+ if (spi->daisy_chain_devs == NULL)
+ return 0;
+
+ if (message->is_dma_mapped) {
+ dev_err(&spi->dev,
+ "DMA mapped transfer is not support when on daisy chain");
+ return -EINVAL;
+ }
+
+ if (!list_is_singular(&message->transfers)) {
+ dev_err(&spi->dev,
+ "Mutliple transfer segments are not support when on daisy chain");
+ return -EINVAL;
+ }
+
+ message->daisy_chain_transfers = message->transfers;
+ INIT_LIST_HEAD(&message->transfers);
+
+ list_for_each_entry(spi_chain_dev, spi->daisy_chain_devs, devices) {
+ if (spi_chain_dev->spi == spi) {
+ tr = list_first_entry(&message->daisy_chain_transfers,
+ struct spi_transfer,
+ transfer_list);
+
+ //check if mode is not being changed
+ if (tr->tx_nbits)
+ switch (spi->mode &
+ (SPI_TX_DUAL | SPI_TX_QUAD)) {
+ case 0:
+ if (!(tr->tx_nbits & SPI_NBITS_SINGLE))
+ goto err_tx_mode;
+ break;
+
+ case SPI_TX_DUAL:
+ if (!(tr->tx_nbits & SPI_NBITS_DUAL))
+ goto err_tx_mode;
+ break;
+
+ case SPI_TX_QUAD:
+ if (!(tr->tx_nbits & SPI_NBITS_QUAD))
+ goto err_tx_mode;
+ break;
+
+ default:
+ goto err_tx_mode;
+ }
+
+ if (tr->rx_nbits)
+ switch (spi->mode &
+ (SPI_RX_DUAL | SPI_RX_QUAD)) {
+ case 0:
+ if (!(tr->rx_nbits & SPI_NBITS_SINGLE))
+ goto err_rx_mode;
+ break;
+
+ case SPI_RX_DUAL:
+ if (tr->rx_nbits & SPI_NBITS_DUAL)
+ goto err_rx_mode;
+ break;
+ case SPI_RX_QUAD:
+ if (tr->rx_nbits & SPI_NBITS_QUAD)
+ goto err_rx_mode;
+ break;
+ default:
+ goto err_rx_mode;
+ }
+
+ //check if frequency is not being changed
+ if (tr->speed_hz && tr->speed_hz != spi->max_speed_hz) {
+ dev_err(&spi->dev,
+ "Change of SPI frequency not supported when on daisy chain");
+ return -EINVAL;
+ }
+
+ //daisy chain operations has a regular length
+ if (tr->len == spi_chain_dev->no_operation.len) {
+ tr->bits_per_word = spi_chain_dev->no_operation
+ .bits_per_word;
+ tr->cs_change = 0;
+
+ list_add_tail(&tr->transfer_list,
+ &message->transfers);
+ }
+ //daisy chain operation has different than regular length
+ else {
+ if (tr->len > spi_chain_dev->no_operation.len) {
+ dev_err(&spi->dev,
+ "Transmission not supported");
+ return -EINVAL;
+ }
+
+ ntr = kzalloc(sizeof(*ntr), GFP_KERNEL);
+
+ if (!ntr)
+ return -ENOMEM;
+
+ message->daisy_chain_new_transfer = ntr;
+
+ ntr->len = spi_chain_dev->no_operation.len;
+ ntr->bits_per_word = spi_chain_dev->no_operation
+ .bits_per_word;
+
+ //copy tx buffer
+ if (tr->tx_buf) {
+ ntr->tx_buf =
+ kmalloc(ntr->len, GFP_KERNEL);
+ if (!ntr->tx_buf) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ //The daisy-chain padding is assumed to be right-justified,
+ //so unused tx bits are transferred first
+ memcpy((void *)((char *)ntr->tx_buf +
+ ntr->len - tr->len),
+ tr->tx_buf, tr->len);
+ }
+
+ //allocate rx buffer
+ if (tr->rx_buf) {
+ ntr->rx_buf =
+ kmalloc(ntr->len, GFP_KERNEL);
+ if (!ntr->rx_buf) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ }
+
+ list_add_tail(&ntr->transfer_list,
+ &message->transfers);
+ }
+ } else
+ list_add_tail(
+ &spi_chain_dev->no_operation.transfer_list,
+ &message->transfers);
+ }
+
+ return 0;
+
+err_out:
+ kfree(ntr->tx_buf);
+ kfree(ntr->rx_buf);
+ kfree(ntr);
+ return rc;
+
+err_tx_mode:
+ dev_err(&spi->dev, "Unsupported tx mode on daisy chain");
+ return -EINVAL;
+
+err_rx_mode:
+ dev_err(&spi->dev, "Unsupported rx mode on daisy chain");
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(spi_daisy_chain_message_pre);
+
+/**
+ * spi_daisy_chain_message_post - generate SPI daisy chain message
+ * @spi: device with which data will be exchanged
+ * @message: describes the data transfers
+ *
+ * The call checks if the SPI device is part of daisy chain. If so, it
+ * removes no-operation codes of unaddressd device of a given chain.
+ **/
+
+void spi_daisy_chain_message_post(struct spi_device *spi,
+ struct spi_message *message)
+{
+ struct spi_transfer *tr;
+
+ //the device is not part of a daisy-chain
+ if (spi->daisy_chain_devs == NULL)
+ return;
+
+ if (message->daisy_chain_new_transfer) {
+ tr = list_first_entry(&message->daisy_chain_transfers,
+ struct spi_transfer, transfer_list);
+ if (tr->rx_buf)
+ //The daisy-chain padding is assumed to be right-justified,
+ //so unused rx bits were received first and can be skipped
+ memcpy((void *)tr->rx_buf,
+ (char *)message->daisy_chain_new_transfer->rx_buf +
+ message->daisy_chain_new_transfer->len -
+ tr->len,
+ tr->len);
+
+ kfree(message->daisy_chain_new_transfer->tx_buf);
+ kfree(message->daisy_chain_new_transfer->rx_buf);
+ kfree(message->daisy_chain_new_transfer);
+ }
+
+ list_del(&message->transfers);
+
+ message->transfers = message->daisy_chain_transfers;
+}
+EXPORT_SYMBOL_GPL(spi_daisy_chain_message_post);
+
+void spi_daisy_chain_clean(struct list_head *daisy_chain_devs)
+{
+ struct spi_device *spi_dev;
+ struct spi_daisy_chain_device *spi_chain_dev;
+
+ list_for_each_entry(spi_chain_dev, daisy_chain_devs, devices) {
+ spi_dev = spi_chain_dev->spi;
+ spi_dev_put(spi_dev);
+ kfree(spi_chain_dev->no_operation.tx_buf);
+ kfree(spi_chain_dev);
+ }
+ list_del(daisy_chain_devs);
+ kfree(daisy_chain_devs);
+}
+
+static int spi_daisy_chain_driver_probe(struct spi_device *spi)
+{
+ struct device_node *nc;
+ struct spi_device *spi_dev;
+ struct spi_daisy_chain_device *spi_chain_dev;
+ struct spi_transfer *no_operation;
+ int w_size;
+ int rc;
+
+ //Initialise the SPI daisy-chain queue
+ spi->daisy_chain_devs =
+ kzalloc(sizeof(*spi->daisy_chain_devs), GFP_KERNEL);
+ if (!spi->daisy_chain_devs)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(spi->daisy_chain_devs);
+
+ for_each_available_child_of_node(spi->dev.of_node, nc) {
+ if (of_node_test_and_set_flag(nc, OF_POPULATED))
+ continue;
+
+ spi_chain_dev = kzalloc(sizeof(*spi_chain_dev), GFP_KERNEL);
+ if (!spi_chain_dev) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ ////////////////////////////////////////////////////////////
+ //extract daisy-chain no-operation command from devicetree
+ ////////////////////////////////////////////////////////////
+ no_operation = &spi_chain_dev->no_operation;
+ if (of_property_read_u8(nc, "spi-daisy-chain-bits_per_word",
+ &no_operation->bits_per_word)) {
+ no_operation->bits_per_word = 8;
+ }
+
+ if (no_operation->bits_per_word > 32) {
+ dev_err(&spi->dev,
+ "device %pOF: 'spi-daisy-chain-bits_per_word' property can't by higher than 32",
+ nc);
+ rc = -EINVAL;
+ goto err_out_spi_chain;
+ }
+
+ if (of_property_read_u32(nc, "spi-daisy-chain-len",
+ &no_operation->len)) {
+ dev_err(&spi->dev,
+ "device %pOF doesn't define 'spi-daisy-chain-len' property",
+ nc);
+ rc = -EINVAL;
+ goto err_out_spi_chain;
+ }
+
+ // SPI transfer length should be multiple of SPI word size
+ // where SPI word size should be power-of-two multiple
+ if (no_operation->bits_per_word <= 8)
+ w_size = 1;
+ else if (no_operation->bits_per_word <= 16)
+ w_size = 2;
+ else
+ w_size = 4;
+
+ /* No partial transfers accepted */
+ if (no_operation->len % w_size) {
+ rc = -EINVAL;
+ dev_err(&spi->dev,
+ "no partial transfers accepted (propeties 'spi-daisy-chain-len' and spi-daisy-chain-bits_per_word of device %pOF",
+ nc);
+ rc = -EINVAL;
+ goto err_out_spi_chain;
+ }
+
+ no_operation->tx_buf = kmalloc(no_operation->len, GFP_KERNEL);
+ if (!no_operation->tx_buf) {
+ rc = -ENOMEM;
+ goto err_out_spi_chain;
+ }
+
+ if (of_property_read_u8_array(nc, "spi-daisy-chain-noop",
+ (void *)no_operation->tx_buf,
+ no_operation->len)) {
+ dev_err(&spi->dev,
+ "device %pOF doesn't define 'spi-daisy-chain-noop' property",
+ nc);
+ rc = -EINVAL;
+ goto err_out_tx_buf;
+ }
+
+ ////////////////////////////
+ //allocate a new SPI device
+ ////////////////////////////
+ spi_dev = spi_alloc_device(spi->controller);
+ if (!spi_dev) {
+ dev_err(&spi->dev, "spi_device alloc error for %pOF\n",
+ nc);
+ rc = -ENOMEM;
+ goto err_out_tx_buf;
+ }
+ spi_chain_dev->spi = spi_dev;
+ spi_dev->daisy_chain_devs = spi->daisy_chain_devs;
+
+ //select device driver
+ rc = of_modalias_node(nc, spi_dev->modalias,
+ sizeof(spi_dev->modalias));
+ if (rc < 0) {
+ dev_err(&spi->dev, "cannot find modalias for %pOF\n",
+ nc);
+ goto err_out_spi_dev;
+ }
+
+ //store a pointer to the node in the device structure
+ of_node_get(nc);
+ spi_dev->dev.of_node = nc;
+
+ //add the SPI device to the chain
+ list_add_tail(&spi_chain_dev->devices, spi->daisy_chain_devs);
+ }
+
+ //////////////////////
+ //add all SPI devices
+ //////////////////////
+ list_for_each_entry(spi_chain_dev, spi->daisy_chain_devs, devices) {
+ spi_dev = spi_chain_dev->spi;
+
+ //All devices on the chain share settings of the daisy-chain node
+ //The individual settings of the SPI nodes are ignored
+ spi_dev->mode = spi->mode;
+ spi_dev->chip_select = spi->chip_select;
+ spi_dev->max_speed_hz = spi->max_speed_hz;
+
+ //Register the new device
+ rc = spi_add_device(spi_dev);
+ if (rc) {
+ dev_err(&spi->dev,
+ "spi_device register error on daisy chain %pOF\n",
+ spi_dev->dev.of_node);
+ of_node_put(nc);
+ goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out_spi_dev:
+ spi_dev_put(spi_chain_dev->spi);
+err_out_tx_buf:
+ kfree(spi_chain_dev->no_operation.tx_buf);
+err_out_spi_chain:
+ kfree(spi_chain_dev);
+err_out:
+ spi_daisy_chain_clean(spi->daisy_chain_devs);
+ return rc;
+}
+
+static int spi_daisy_chain_driver_remove(struct spi_device *spi)
+{
+ spi_daisy_chain_clean(spi->daisy_chain_devs);
+ return 0;
+}
+
+static const struct of_device_id spi_daisy_chain_of_match[] = {
+ {
+ .compatible = "spi,daisy_chain",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, spi_daisy_chain_of_match);
+
+static struct spi_driver spi_daisy_chain_driver = {
+ .probe = spi_daisy_chain_driver_probe,
+ .remove = spi_daisy_chain_driver_remove,
+ .driver = {
+ .name = "daisy_chain",
+ .of_match_table = spi_daisy_chain_of_match,
+ },
+};
+module_spi_driver(spi_daisy_chain_driver);
+
+MODULE_AUTHOR("Adrian Fiergolski <Adrian.Fiergolski@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Driver handling SPI daisy chain");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:daisy_chain");
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 8158e281f354..96c8ce572e2f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -18,6 +18,7 @@
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
+#include <linux/spi/spi-daisy_chain.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
@@ -522,14 +523,34 @@ EXPORT_SYMBOL_GPL(spi_alloc_device);
static void spi_dev_set_name(struct spi_device *spi)
{
struct acpi_device *adev = ACPI_COMPANION(&spi->dev);
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ struct spi_daisy_chain_device *spi_chain_dev;
+ int spi_dev_index;
+#endif
if (adev) {
dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
return;
}
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev),
- spi->chip_select);
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ if (spi->daisy_chain_devs != NULL) {
+ spi_dev_index = 0;
+ list_for_each_entry(spi_chain_dev, spi->daisy_chain_devs,
+ devices){
+ if (spi_chain_dev->spi == spi)
+ break;
+ spi_dev_index++;
+ }
+
+ dev_set_name(&spi->dev, "%s.%u.%u",
+ dev_name(&spi->controller->dev), spi->chip_select,
+ spi_dev_index);
+
+ } else
+#endif
+ dev_set_name(&spi->dev, "%s.%u",
+ dev_name(&spi->controller->dev), spi->chip_select);
}
static int spi_dev_check(struct device *dev, void *data)
@@ -573,15 +594,27 @@ int spi_add_device(struct spi_device *spi)
* chipselect **BEFORE** we call setup(), else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
- mutex_lock(&spi_add_lock);
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ /* Do not lock the controller when registering the daisy_chain driver
+ * as the last one wouldn't be able to register its subnodes
+ */
+ if (strcmp((const char *)spi->dev.of_node->name, "daisy_chain") == 0)
+#endif
+ mutex_lock(&spi_add_lock);
- status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
- if (status) {
- dev_err(dev, "chipselect %d already in use\n",
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ if (spi->daisy_chain_devs == NULL) {
+#endif
+ status = bus_for_each_dev(&spi_bus_type, NULL, spi,
+ spi_dev_check);
+ if (status) {
+ dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
- goto done;
+ goto done;
+ }
+#ifdef CONFIG_SPI_DAISY_CHAIN
}
-
+#endif
/* Descriptors take precedence */
if (ctlr->cs_gpiods)
spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
@@ -608,7 +641,10 @@ int spi_add_device(struct spi_device *spi)
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
- mutex_unlock(&spi_add_lock);
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ if (strcmp((const char *)spi->dev.of_node->name, "daisy_chain") == 0)
+#endif
+ mutex_unlock(&spi_add_lock);
return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);
@@ -3694,6 +3730,12 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message)
SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_sync);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ status = spi_daisy_chain_message_pre(spi, message);
+ if (status < 0)
+ return status;
+#endif
+
/* If we're not using the legacy transfer method then we will
* try to transfer in the calling context so special case.
* This code would be less tricky if we could remove the
@@ -3727,6 +3769,11 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message)
status = message->status;
}
message->context = NULL;
+
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ spi_daisy_chain_message_post(spi, message);
+#endif
+
return status;
}
diff --git a/include/linux/spi/spi-daisy_chain.h b/include/linux/spi/spi-daisy_chain.h
new file mode 100644
index 000000000000..8967292863b7
--- /dev/null
+++ b/include/linux/spi/spi-daisy_chain.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * A driver handling the SPI daisy chaines.
+ *
+ * Copyright (C) 2020 Fastree3D
+ * Adrian Fiergolski <Adrian.Fiergolski@xxxxxxxxxxxxx>
+ */
+
+#ifndef __LINUX_SPI_DAISY_CHAIN_H
+#define __LINUX_SPI_DAISY_CHAIN_H
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+/**
+ * struct spi_daisy_chain - interface to a SPI device on a daisy chain
+ * @spi: SPI device on the daisy chain
+ * @no_operation: no-opeartion SPI message for the given device
+ * @devices: list of SPI devices sharing the given daisy chain
+ **/
+struct spi_daisy_chain_device {
+ struct spi_device *spi;
+ struct spi_transfer no_operation;
+ struct list_head devices;
+};
+
+extern int spi_daisy_chain_message_pre(struct spi_device *spi,
+ struct spi_message *message);
+extern void spi_daisy_chain_message_post(struct spi_device *spi,
+ struct spi_message *message);
+
+#endif /* __LINUX_SPI_DAISY_CHAIN_H */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index aac57b5b7c21..bdc6973606dd 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -111,6 +111,9 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
* @dev: Driver model representation of the device.
* @controller: SPI controller used with the device.
* @master: Copy of controller, for backwards compatibility.
+ * @daisy_chain_devs: list of all SPI devices on the daisy chain
+ * used by the given SPI device.
+ * Handled by the SPI daisy chain driver.
* @max_speed_hz: Maximum clock rate to be used with this chip
* (on this board); may be changed by the device's driver.
* The spi_transfer.speed_hz can override this for each transfer.
@@ -160,6 +163,9 @@ struct spi_device {
struct device dev;
struct spi_controller *controller;
struct spi_controller *master; /* compatibility layer */
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ struct list_head *daisy_chain_devs;
+#endif
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
@@ -945,6 +951,11 @@ struct spi_transfer {
/**
* struct spi_message - one multi-segment SPI transaction
* @transfers: list of transfer segments in this transaction
+ * @daisy_chain_transfers: head of the original transfers queue.
+ * Handled by the SPI daisy chain driver.
+ * @daisy_chain_new_transfer: pointer to an extra SPI transfer,
+ * in case it had to be created.
+ * Handled by the SPI daisy chain driver.
* @spi: SPI device to which the transaction is queued
* @is_dma_mapped: if true, the caller provided both dma and cpu virtual
* addresses for each transfer buffer
@@ -975,7 +986,11 @@ struct spi_transfer {
struct spi_message {
struct list_head transfers;
- struct spi_device *spi;
+#ifdef CONFIG_SPI_DAISY_CHAIN
+ struct list_head daisy_chain_transfers;
+ struct spi_transfer *daisy_chain_new_transfer;
+#endif
+ struct spi_device *spi;
unsigned is_dma_mapped:1;
--
2.27.0