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.fiergol...@fastree3d.com>
---
Changes in v2:
- declare spi_daisy_chain_clean as static
Reported-by: kernel test robot <l...@intel.com>
---
 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..f187502b1861
--- /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.fiergol...@fastree3d.com>
+ */
+
+#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);
+
+static 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.fiergol...@fastree3d.com>");
+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.fiergol...@fastree3d.com>
+ */
+
+#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

Reply via email to