*/
 
 #include <linux/clk.h>
+#include <linux/idr.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/of.h>
        uint16_t instructions[];
 };
 
-struct spi_engine {
-       struct clk *clk;
-       struct clk *ref_clk;
-
-       spinlock_t lock;
-
-       void __iomem *base;
-
-       struct spi_message *msg;
+/**
+ * struct spi_engine_message_state - SPI engine per-message state
+ */
+struct spi_engine_message_state {
+       /** Instructions for executing this message. */
        struct spi_engine_program *p;
+       /** Number of elements in cmd_buf array. */
        unsigned cmd_length;
+       /** Array of commands not yet written to CMD FIFO. */
        const uint16_t *cmd_buf;
-
+       /** Next xfer with tx_buf not yet fully written to TX FIFO. */
        struct spi_transfer *tx_xfer;
+       /** Size of tx_buf in bytes. */
        unsigned int tx_length;
+       /** Bytes not yet written to TX FIFO. */
        const uint8_t *tx_buf;
-
+       /** Next xfer with rx_buf not yet fully written to RX FIFO. */
        struct spi_transfer *rx_xfer;
+       /** Size of tx_buf in bytes. */
        unsigned int rx_length;
+       /** Bytes not yet written to the RX FIFO. */
        uint8_t *rx_buf;
+       /** ID to correlate SYNC interrupts with this message. */
+       u8 sync_id;
+};
+
+struct spi_engine {
+       struct clk *clk;
+       struct clk *ref_clk;
 
-       unsigned int sync_id;
+       spinlock_t lock;
+
+       void __iomem *base;
+
+       struct spi_message *msg;
+       struct ida sync_ida;
        unsigned int completed_id;
 
        unsigned int int_enable;
 
 static void spi_engine_tx_next(struct spi_engine *spi_engine)
 {
-       struct spi_transfer *xfer = spi_engine->tx_xfer;
+       struct spi_engine_message_state *st = spi_engine->msg->state;
+       struct spi_transfer *xfer = st->tx_xfer;
 
        do {
                spi_engine_xfer_next(spi_engine, &xfer);
        } while (xfer && !xfer->tx_buf);
 
-       spi_engine->tx_xfer = xfer;
+       st->tx_xfer = xfer;
        if (xfer) {
-               spi_engine->tx_length = xfer->len;
-               spi_engine->tx_buf = xfer->tx_buf;
+               st->tx_length = xfer->len;
+               st->tx_buf = xfer->tx_buf;
        } else {
-               spi_engine->tx_buf = NULL;
+               st->tx_buf = NULL;
        }
 }
 
 static void spi_engine_rx_next(struct spi_engine *spi_engine)
 {
-       struct spi_transfer *xfer = spi_engine->rx_xfer;
+       struct spi_engine_message_state *st = spi_engine->msg->state;
+       struct spi_transfer *xfer = st->rx_xfer;
 
        do {
                spi_engine_xfer_next(spi_engine, &xfer);
        } while (xfer && !xfer->rx_buf);
 
-       spi_engine->rx_xfer = xfer;
+       st->rx_xfer = xfer;
        if (xfer) {
-               spi_engine->rx_length = xfer->len;
-               spi_engine->rx_buf = xfer->rx_buf;
+               st->rx_length = xfer->len;
+               st->rx_buf = xfer->rx_buf;
        } else {
-               spi_engine->rx_buf = NULL;
+               st->rx_buf = NULL;
        }
 }
 
 static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
 {
        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
+       struct spi_engine_message_state *st = spi_engine->msg->state;
        unsigned int n, m, i;
        const uint16_t *buf;
 
        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
-       while (n && spi_engine->cmd_length) {
-               m = min(n, spi_engine->cmd_length);
-               buf = spi_engine->cmd_buf;
+       while (n && st->cmd_length) {
+               m = min(n, st->cmd_length);
+               buf = st->cmd_buf;
                for (i = 0; i < m; i++)
                        writel_relaxed(buf[i], addr);
-               spi_engine->cmd_buf += m;
-               spi_engine->cmd_length -= m;
+               st->cmd_buf += m;
+               st->cmd_length -= m;
                n -= m;
        }
 
-       return spi_engine->cmd_length != 0;
+       return st->cmd_length != 0;
 }
 
 static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
 {
        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;
+       struct spi_engine_message_state *st = spi_engine->msg->state;
        unsigned int n, m, i;
        const uint8_t *buf;
 
        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM);
-       while (n && spi_engine->tx_length) {
-               m = min(n, spi_engine->tx_length);
-               buf = spi_engine->tx_buf;
+       while (n && st->tx_length) {
+               m = min(n, st->tx_length);
+               buf = st->tx_buf;
                for (i = 0; i < m; i++)
                        writel_relaxed(buf[i], addr);
-               spi_engine->tx_buf += m;
-               spi_engine->tx_length -= m;
+               st->tx_buf += m;
+               st->tx_length -= m;
                n -= m;
-               if (spi_engine->tx_length == 0)
+               if (st->tx_length == 0)
                        spi_engine_tx_next(spi_engine);
        }
 
-       return spi_engine->tx_length != 0;
+       return st->tx_length != 0;
 }
 
 static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine)
 {
        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO;
+       struct spi_engine_message_state *st = spi_engine->msg->state;
        unsigned int n, m, i;
        uint8_t *buf;
 
        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL);
-       while (n && spi_engine->rx_length) {
-               m = min(n, spi_engine->rx_length);
-               buf = spi_engine->rx_buf;
+       while (n && st->rx_length) {
+               m = min(n, st->rx_length);
+               buf = st->rx_buf;
                for (i = 0; i < m; i++)
                        buf[i] = readl_relaxed(addr);
-               spi_engine->rx_buf += m;
-               spi_engine->rx_length -= m;
+               st->rx_buf += m;
+               st->rx_length -= m;
                n -= m;
-               if (spi_engine->rx_length == 0)
+               if (st->rx_length == 0)
                        spi_engine_rx_next(spi_engine);
        }
 
-       return spi_engine->rx_length != 0;
+       return st->rx_length != 0;
 }
 
 static irqreturn_t spi_engine_irq(int irq, void *devid)
                        disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
        }
 
-       if (pending & SPI_ENGINE_INT_SYNC) {
-               if (spi_engine->msg &&
-                   spi_engine->completed_id == spi_engine->sync_id) {
+       if (pending & SPI_ENGINE_INT_SYNC && spi_engine->msg) {
+               struct spi_engine_message_state *st = spi_engine->msg->state;
+
+               if (spi_engine->completed_id == st->sync_id) {
                        struct spi_message *msg = spi_engine->msg;
+                       struct spi_engine_message_state *st = msg->state;
 
-                       kfree(spi_engine->p);
+                       ida_free(&spi_engine->sync_ida, st->sync_id);
+                       kfree(st->p);
+                       kfree(st);
                        msg->status = 0;
                        msg->actual_length = msg->frame_length;
                        spi_engine->msg = NULL;
 {
        struct spi_engine_program p_dry, *p;
        struct spi_engine *spi_engine = spi_controller_get_devdata(host);
+       struct spi_engine_message_state *st;
        unsigned int int_enable = 0;
        unsigned long flags;
        size_t size;
+       int ret;
+
+       st = kzalloc(sizeof(*st), GFP_KERNEL);
+       if (!st)
+               return -ENOMEM;
 
        p_dry.length = 0;
        spi_engine_compile_message(spi_engine, msg, true, &p_dry);
 
        size = sizeof(*p->instructions) * (p_dry.length + 1);
        p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
-       if (!p)
+       if (!p) {
+               kfree(st);
                return -ENOMEM;
+       }
+
+       ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL);
+       if (ret < 0) {
+               kfree(p);
+               kfree(st);
+               return ret;
+       }
+
+       st->sync_id = ret;
+
        spi_engine_compile_message(spi_engine, msg, false, p);
 
        spin_lock_irqsave(&spi_engine->lock, flags);
-       spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff;
-       spi_engine_program_add_cmd(p, false,
-               SPI_ENGINE_CMD_SYNC(spi_engine->sync_id));
+       spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id));
 
+       msg->state = st;
        spi_engine->msg = msg;
-       spi_engine->p = p;
+       st->p = p;
 
-       spi_engine->cmd_buf = p->instructions;
-       spi_engine->cmd_length = p->length;
+       st->cmd_buf = p->instructions;
+       st->cmd_length = p->length;
        if (spi_engine_write_cmd_fifo(spi_engine))
                int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
 
                int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
 
        spi_engine_rx_next(spi_engine);
-       if (spi_engine->rx_length != 0)
+       if (st->rx_length != 0)
                int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
 
        int_enable |= SPI_ENGINE_INT_SYNC;
        spi_engine = spi_controller_get_devdata(host);
 
        spin_lock_init(&spi_engine->lock);
+       ida_init(&spi_engine->sync_ida);
 
        spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
        if (IS_ERR(spi_engine->clk))