Add a SPI driver for Qualcomm's GENI hardware.

Signed-off-by: Stephen Boyd <swb...@chromium.org>
---
 drivers/spi/Kconfig         |  10 +
 drivers/spi/Makefile        |   1 +
 drivers/spi/spi-geni-qcom.c | 527 ++++++++++++++++++++++++++++++++++++
 3 files changed, 538 insertions(+)
 create mode 100644 drivers/spi/spi-geni-qcom.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 96ea033082b5..29e84098cb2f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -414,6 +414,16 @@ config SPI_QUP
          mode supports up to 50MHz, up to four chip selects, programmable
          data path from 4 bits to 32 bits and numerous protocol variants.
 
+config SPI_GENI_QCOM
+       bool "Qualcomm Generic Interface (GENI) SPI controller"
+       depends on ARCH_SNAPDRAGON
+       help
+         Support for the Qualcomm Generic Interface (GENI) SPI controller.
+         The Generic Interface (GENI) is a firmware based Qualcomm Universal
+         Peripherals (QUP) Serial Engine (SE) Wrapper which can support 
multiple
+         bus protocols depending on the firmware type loaded at early boot time
+         based on system configuration.
+
 config RENESAS_RPC_SPI
        bool "Renesas RPC SPI driver"
        depends on RCAR_64 || RZA1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7051e2a00c60..385e5b87d675 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
 obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
 obj-$(CONFIG_PL022_SPI) += pl022_spi.o
 obj-$(CONFIG_SPI_QUP) += spi-qup.o
+obj-$(CONFIG_SPI_GENI_QCOM) += spi-geni-qcom.o
 obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
 obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
 obj-$(CONFIG_ROCKCHIP_SFC) += rockchip_sfc.o
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
new file mode 100644
index 000000000000..9371f1530c9f
--- /dev/null
+++ b/drivers/spi/spi-geni-qcom.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/iopoll.h>
+#include <spi.h>
+#include <log.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <dm/read.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <time.h>
+#include <soc/qcom/geni-se.h>
+
+/* SPI SE specific registers and respective register fields */
+#define SE_SPI_CPHA            0x224
+#define CPHA                   BIT(0)
+
+#define SE_SPI_LOOPBACK                0x22c
+#define LOOPBACK_ENABLE                0x1
+#define NORMAL_MODE            0x0
+#define LOOPBACK_MSK           GENMASK(1, 0)
+
+#define SE_SPI_CPOL            0x230
+#define CPOL                   BIT(2)
+
+#define SE_SPI_DEMUX_OUTPUT_INV        0x24c
+
+#define SE_SPI_DEMUX_SEL       0x250
+
+#define SE_SPI_TRANS_CFG       0x25c
+#define CS_TOGGLE              BIT(1)
+
+#define SE_SPI_WORD_LEN                0x268
+#define WORD_LEN_MSK           GENMASK(9, 0)
+#define MIN_WORD_LEN           4
+
+#define SE_SPI_TX_TRANS_LEN    0x26c
+#define SE_SPI_RX_TRANS_LEN    0x270
+#define TRANS_LEN_MSK          GENMASK(23, 0)
+
+#define SE_SPI_PRE_POST_CMD_DLY        0x274
+
+#define SE_SPI_DELAY_COUNTERS  0x278
+#define SPI_INTER_WORDS_DELAY_MSK      GENMASK(9, 0)
+#define SPI_CS_CLK_DELAY_MSK           GENMASK(19, 10)
+#define SPI_CS_CLK_DELAY_SHFT          10
+
+#define SE_GENI_M_IRQ_CLEAR    0x618
+#define SE_GENI_M_IRQ_STATUS   0x610
+
+/* M_CMD OP codes for SPI */
+#define SPI_TX_ONLY            1
+#define SPI_RX_ONLY            2
+#define SPI_TX_RX              7
+#define SPI_CS_ASSERT          8
+#define SPI_CS_DEASSERT                9
+#define SPI_SCK_ONLY           10
+/* M_CMD params for SPI */
+#define SPI_PRE_CMD_DELAY      BIT(0)
+#define TIMESTAMP_BEFORE       BIT(1)
+#define FRAGMENTATION          BIT(2)
+#define TIMESTAMP_AFTER                BIT(3)
+#define POST_CMD_DELAY         BIT(4)
+
+#define BYTES_PER_FIFO_WORD    4U
+
+struct geni_spi_priv {
+       fdt_addr_t wrapper;
+       phys_addr_t base;
+       struct clk clk;
+       u32 tx_depth;
+       bool cs_high;
+};
+
+static void geni_se_setup_m_cmd(struct geni_spi_priv *priv, u32 cmd, u32 
params)
+{
+       u32 m_cmd;
+
+       debug("%s: cmd=%#x, parms=%#x\n", __func__, cmd, params);
+       m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
+       writel(m_cmd, priv->base + SE_GENI_M_CMD0);
+}
+
+static void handle_se_timeout(struct geni_spi_priv *priv)
+{
+       int ret;
+       u32 m_irq;
+
+       writel(0, priv->base + SE_GENI_TX_WATERMARK_REG);
+
+       writel(M_CMD_CANCEL_EN, priv->base + SE_GENI_M_CMD_CTRL_REG);
+
+       ret = readl_poll_timeout(priv->base + SE_GENI_M_IRQ_STATUS, m_irq,
+                                (m_irq & M_CMD_CANCEL_EN) == M_CMD_CANCEL_EN,
+                                100);
+       writel(M_CMD_CANCEL_EN, priv->base + SE_GENI_M_IRQ_CLEAR);
+       if (ret < 0) {
+               printf("spi-geni-qcom: Cancel failed. Abort the operation\n");
+
+               writel_relaxed(M_CMD_ABORT_EN, priv->base + 
SE_GENI_M_CMD_CTRL_REG);
+               ret = readl_poll_timeout(priv->base + SE_GENI_M_IRQ_STATUS, 
m_irq,
+                                        (m_irq & M_CMD_ABORT_EN) == 
M_CMD_ABORT_EN,
+                                        100);
+               writel(M_CMD_ABORT_EN, priv->base + SE_GENI_M_IRQ_CLEAR);
+               if (ret < 0)
+                       printf("spi-geni-qcom: Abort failed\n");
+       }
+}
+
+static int geni_spi_set_speed(struct udevice *dev, uint speed)
+{
+       /* TODO: Set a clk frequency or change divider here */
+       return 0;
+}
+
+static int geni_spi_set_mode(struct udevice *bus, uint mode)
+{
+       struct geni_spi_priv *priv = dev_get_priv(bus);
+       u32 loopback_cfg = 0, cpol = 0, cpha = 0;
+
+       if (mode & SPI_LOOP)
+               loopback_cfg = LOOPBACK_ENABLE;
+
+       if (mode & SPI_CPOL)
+               cpol = CPOL;
+
+       if (mode & SPI_CPHA)
+               cpha = CPHA;
+
+       if (mode & SPI_CS_HIGH)
+               priv->cs_high = true;
+
+       writel(loopback_cfg, priv->base + SE_SPI_LOOPBACK);
+       writel(cpha, priv->base + SE_SPI_CPHA);
+       writel(cpol, priv->base + SE_SPI_CPOL);
+
+       return 0;
+}
+
+static void geni_spi_reset(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct geni_spi_priv *priv = dev_get_priv(bus);
+
+       /* Driver may not be probed yet */
+       if (!priv)
+               return;
+}
+
+#define NUM_PACKING_VECTORS 4
+#define PACKING_START_SHIFT 5
+#define PACKING_DIR_SHIFT 4
+#define PACKING_LEN_SHIFT 1
+#define PACKING_STOP_BIT BIT(0)
+#define PACKING_VECTOR_SHIFT 10
+static void geni_spi_config_packing(struct geni_spi_priv *geni, int bpw,
+                                   int pack_words, bool msb_to_lsb,
+                                   bool tx_cfg, bool rx_cfg)
+{
+       u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {0};
+       int len;
+       int temp_bpw = bpw;
+       int idx_start = msb_to_lsb ? bpw - 1 : 0;
+       int idx = idx_start;
+       int idx_delta = msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE;
+       int ceil_bpw = ALIGN(bpw, BITS_PER_BYTE);
+       int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
+       int i;
+
+       if (iter <= 0 || iter > NUM_PACKING_VECTORS)
+               return;
+
+       for (i = 0; i < iter; i++) {
+               len = min_t(int, temp_bpw, BITS_PER_BYTE) - 1;
+               cfg[i] = idx << PACKING_START_SHIFT;
+               cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
+               cfg[i] |= len << PACKING_LEN_SHIFT;
+
+               if (temp_bpw <= BITS_PER_BYTE) {
+                       idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
+                       temp_bpw = bpw;
+               } else {
+                       idx = idx + idx_delta;
+                       temp_bpw = temp_bpw - BITS_PER_BYTE;
+               }
+       }
+       cfg[iter - 1] |= PACKING_STOP_BIT;
+       cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
+       cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
+
+       if (tx_cfg) {
+               writel(cfg0, geni->base + SE_GENI_TX_PACKING_CFG0);
+               writel(cfg1, geni->base + SE_GENI_TX_PACKING_CFG1);
+       }
+       if (rx_cfg) {
+               writel(cfg0, geni->base + SE_GENI_RX_PACKING_CFG0);
+               writel(cfg1, geni->base + SE_GENI_RX_PACKING_CFG1);
+       }
+
+       /*
+        * Number of protocol words in each FIFO entry
+        * 0 - 4x8, four words in each entry, max word size of 8 bits
+        * 1 - 2x16, two words in each entry, max word size of 16 bits
+        * 2 - 1x32, one word in each entry, max word size of 32 bits
+        * 3 - undefined
+        */
+       if (pack_words || bpw == 32)
+               writel(bpw / 16, geni->base + SE_GENI_BYTE_GRAN);
+}
+
+static u32 geni_spi_get_tx_fifo_depth(struct geni_spi_priv *geni)
+{
+       u32 val, hw_version, hw_major, hw_minor, tx_fifo_depth_mask;
+
+       hw_version = readl(geni->wrapper + QUP_HW_VER_REG);
+       hw_major = GENI_SE_VERSION_MAJOR(hw_version);
+       hw_minor = GENI_SE_VERSION_MINOR(hw_version);
+
+       if ((hw_major == 3 && hw_minor >= 10) || hw_major > 3)
+               tx_fifo_depth_mask = TX_FIFO_DEPTH_MSK_256_BYTES;
+       else
+               tx_fifo_depth_mask = TX_FIFO_DEPTH_MSK;
+
+       val = readl(geni->base + SE_HW_PARAM_0);
+
+       return (val & tx_fifo_depth_mask) >> TX_FIFO_DEPTH_SHFT;
+}
+
+static void geni_spi_drain_rx(struct geni_spi_priv *geni)
+{
+       u32 rx_fifo_status;
+       unsigned int rx_bytes;
+       unsigned int rx_last_byte_valid;
+       unsigned int i;
+
+       rx_fifo_status = readl(geni->base + SE_GENI_RX_FIFO_STATUS);
+       rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * BYTES_PER_FIFO_WORD;
+       if (rx_fifo_status & RX_LAST) {
+               rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK;
+               rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT;
+               if (rx_last_byte_valid && rx_last_byte_valid < 4)
+                       rx_bytes -= BYTES_PER_FIFO_WORD - rx_last_byte_valid;
+       }
+
+       for (i = 0; i < DIV_ROUND_UP(rx_bytes, BYTES_PER_FIFO_WORD); i++)
+               readl(geni->base + SE_GENI_RX_FIFOn);
+}
+
+static void geni_spi_hw_init(struct udevice *dev)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct geni_spi_priv *geni = dev_get_priv(bus);
+       u32 demux_output_inv = 0;
+       u32 val, demux_sel;
+
+       writel(0, geni->base + SE_GSI_EVENT_EN);
+       writel(0xffffffff, geni->base + SE_GENI_M_IRQ_CLEAR);
+       writel(0xffffffff, geni->base + SE_GENI_S_IRQ_CLEAR);
+       writel(0xffffffff, geni->base + SE_IRQ_EN);
+
+       val = readl(geni->base + GENI_CGC_CTRL);
+       val |= DEFAULT_CGC_EN;
+       writel(val, geni->base + GENI_CGC_CTRL);
+
+       writel(DEFAULT_IO_OUTPUT_CTRL_MSK, geni->base + GENI_OUTPUT_CTRL);
+       writel(FORCE_DEFAULT, geni->base + GENI_FORCE_DEFAULT_REG);
+
+       val = readl(geni->base + SE_IRQ_EN);
+       val |= GENI_M_IRQ_EN | GENI_S_IRQ_EN;
+       writel(val, geni->base + SE_IRQ_EN);
+
+       val = readl(geni->base + SE_GENI_DMA_MODE_EN);
+       val &= ~GENI_DMA_MODE_EN;
+       writel(val, geni->base + SE_GENI_DMA_MODE_EN);
+
+       writel(0, geni->base + SE_GSI_EVENT_EN);
+
+       writel(geni->tx_depth - 3, geni->base + SE_GENI_RX_WATERMARK_REG);
+       writel(geni->tx_depth - 2, geni->base + SE_GENI_RX_RFR_WATERMARK_REG);
+
+       val = readl(geni->base + SE_GENI_M_IRQ_EN);
+       val |= M_COMMON_GENI_M_IRQ_EN;
+       val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN;
+       val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+       writel(val, geni->base + SE_GENI_M_IRQ_EN);
+
+       val = readl(geni->base + SE_GENI_S_IRQ_EN);
+       val |= S_COMMON_GENI_S_IRQ_EN;
+       writel(val, geni->base + SE_GENI_S_IRQ_EN);
+
+       if (geni->cs_high)
+               demux_output_inv = BIT(spi_chip_select(dev));
+       demux_sel = spi_chip_select(dev);
+       writel(demux_sel, geni->base + SE_SPI_DEMUX_SEL);
+       writel(demux_output_inv, geni->base + SE_SPI_DEMUX_OUTPUT_INV);
+
+       writel(((8 - MIN_WORD_LEN) & WORD_LEN_MSK), geni->base + 
SE_SPI_WORD_LEN);
+       geni_spi_config_packing(geni, BITS_PER_BYTE, 4, true, true, true);
+
+       geni_spi_drain_rx(geni);
+}
+
+static int geni_spi_claim_bus(struct udevice *dev)
+{
+       geni_spi_hw_init(dev);
+       return 0;
+}
+
+static int geni_spi_release_bus(struct udevice *dev)
+{
+       /* Reset the SPI hardware */
+       geni_spi_reset(dev);
+
+       return 0;
+}
+
+static int geni_spi_set_cs(struct udevice *bus, bool enable)
+{
+       struct geni_spi_priv *priv = dev_get_priv(bus);
+       u32 m_cmd = 0, m_irq;
+       int ret;
+
+       debug("%s: enable=%d\n", __func__, enable);
+
+       m_cmd = enable ? SPI_CS_ASSERT : SPI_CS_DEASSERT;
+       geni_se_setup_m_cmd(priv, m_cmd, 0);
+
+       ret = readl_poll_timeout(priv->base + SE_GENI_M_IRQ_STATUS, m_irq,
+                                (m_irq & M_CMD_DONE_EN) == M_CMD_DONE_EN,
+                                100);
+       writel(M_CMD_DONE_EN, priv->base + SE_GENI_M_IRQ_CLEAR);
+       if (ret) {
+               printf("spi-geni-qcom: Timeout setting cs\n");
+               handle_se_timeout(priv);
+               return ret;
+       }
+
+       return 0;
+}
+
+static unsigned int
+geni_spi_handle_tx(struct geni_spi_priv *geni, const u8 *dout, unsigned int 
tx_rem_bytes)
+{
+       unsigned int max_bytes;
+       unsigned int i = 0;
+
+       max_bytes = (geni->tx_depth - 1) * BYTES_PER_FIFO_WORD;
+       if (tx_rem_bytes < max_bytes)
+               max_bytes = tx_rem_bytes;
+
+       while (i < max_bytes) {
+               unsigned int j;
+               unsigned int bytes_to_write;
+               u32 fifo_word = 0;
+               u8 *fifo_byte = (u8 *)&fifo_word;
+
+               bytes_to_write = min(BYTES_PER_FIFO_WORD, max_bytes - i);
+               for (j = 0; j < bytes_to_write; j++)
+                       fifo_byte[j] = dout[i++];
+               iowrite32_rep((void *)(geni->base + SE_GENI_TX_FIFOn), 
&fifo_word, 1);
+       }
+       tx_rem_bytes -= max_bytes;
+       if (!tx_rem_bytes)
+               writel(0, geni->base + SE_GENI_TX_WATERMARK_REG);
+
+       return max_bytes;
+}
+
+static int geni_spi_handle_rx(struct geni_spi_priv *geni, u8 *din, unsigned 
int rx_rem_bytes)
+{
+       u32 rx_fifo_status;
+       unsigned int rx_bytes;
+       unsigned int rx_last_byte_valid;
+       unsigned int i = 0;
+
+       rx_fifo_status = readl(geni->base + SE_GENI_RX_FIFO_STATUS);
+       rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * BYTES_PER_FIFO_WORD;
+       if (rx_fifo_status & RX_LAST) {
+               rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK;
+               rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT;
+               if (rx_last_byte_valid && rx_last_byte_valid < 4)
+                       rx_bytes -= BYTES_PER_FIFO_WORD - rx_last_byte_valid;
+       }
+
+       if (rx_rem_bytes < rx_bytes)
+               rx_bytes = rx_rem_bytes;
+
+       while (i < rx_bytes) {
+               u32 fifo_word = 0;
+               u8 *fifo_byte = (u8 *)&fifo_word;
+               unsigned int bytes_to_read;
+               unsigned int j;
+
+               bytes_to_read = min(BYTES_PER_FIFO_WORD, rx_bytes - i);
+               ioread32_rep((void *)(geni->base + SE_GENI_RX_FIFOn), 
&fifo_word, 1);
+               for (j = 0; j < bytes_to_read; j++)
+                       din[i++] = fifo_byte[j];
+       }
+
+       return rx_bytes;
+}
+
+static int geni_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                        const void *dout, void *din, unsigned long flags)
+{
+       struct udevice *bus = dev_get_parent(dev);
+       struct geni_spi_priv *priv = dev_get_priv(bus);
+       unsigned int len = bitlen >> 3;
+       unsigned int rx_rem_bytes = din ? len : 0;
+       unsigned int tx_rem_bytes = dout ? len : 0;
+       int ret = 0;
+       u32 m_cmd = 0, m_irq;
+       ulong start;
+
+       if (len & ~TRANS_LEN_MSK) {
+               printf("spi-geni-qcom: transfer length too long (%d)\n", len);
+               return -EINVAL;
+       }
+
+       if (flags & SPI_XFER_BEGIN) {
+               geni_spi_hw_init(dev);
+               ret = geni_spi_set_cs(bus, true);
+               if (ret != 0)
+                       return ret;
+       }
+
+       if (len) {
+               if (din) {
+                       m_cmd |= SPI_RX_ONLY;
+                       writel(len, priv->base + SE_SPI_RX_TRANS_LEN);
+               }
+               if (dout) {
+                       m_cmd |= SPI_TX_ONLY;
+                       writel(len, priv->base + SE_SPI_TX_TRANS_LEN);
+                       writel(1, priv->base + SE_GENI_TX_WATERMARK_REG);
+               }
+
+               geni_se_setup_m_cmd(priv, m_cmd, FRAGMENTATION);
+
+               start = get_timer(0);
+               do {
+                       ret = readl_poll_timeout(priv->base + 
SE_GENI_M_IRQ_STATUS, m_irq,
+                                                m_irq != 0, 1000);
+                       if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & 
M_RX_FIFO_LAST_EN)) {
+                               rx_rem_bytes -= geni_spi_handle_rx(priv, din + 
len - rx_rem_bytes,
+                                                                  
rx_rem_bytes);
+                       }
+                       if (m_irq & M_TX_FIFO_WATERMARK_EN) {
+                               tx_rem_bytes -= geni_spi_handle_tx(priv, dout + 
len - tx_rem_bytes,
+                                                                  
tx_rem_bytes);
+                       }
+                       writel(m_irq, priv->base + SE_GENI_M_IRQ_CLEAR);
+                       if (m_irq & M_CMD_DONE_EN)
+                               break;
+               } while (get_timer(start) < 100000);
+
+               if (!(m_irq & M_CMD_DONE_EN) || tx_rem_bytes || rx_rem_bytes) {
+                       printf("spi-geni-qcom: Transfer failed\n");
+                       handle_se_timeout(priv);
+                       return -ETIMEDOUT;
+               }
+       }
+
+       if (flags & SPI_XFER_END) {
+               ret = geni_spi_set_cs(bus, false);
+               if (ret != 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static const struct dm_spi_ops geni_spi_ops = {
+       .claim_bus      = geni_spi_claim_bus,
+       .release_bus    = geni_spi_release_bus,
+       .xfer           = geni_spi_xfer,
+       .set_speed      = geni_spi_set_speed,
+       .set_mode       = geni_spi_set_mode,
+};
+
+static int geni_spi_probe(struct udevice *dev)
+{
+       ofnode parent_node = ofnode_get_parent(dev_ofnode(dev));
+       struct geni_spi_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       priv->base = dev_read_addr(dev);
+       if (priv->base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       priv->wrapper = ofnode_get_addr(parent_node);
+       if (priv->wrapper == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       ret = clk_get_by_index(dev, 0, &priv->clk);
+       if (ret)
+               return ret;
+
+       ret = clk_enable(&priv->clk);
+       if (ret < 0)
+               return ret;
+
+       priv->tx_depth = geni_spi_get_tx_fifo_depth(priv);
+
+       return 0;
+}
+
+static const struct udevice_id spi_geni_ids[] = {
+       { .compatible = "qcom,geni-spi" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(geni_spi) = {
+       .name           = "geni_spi",
+       .id             = UCLASS_SPI,
+       .of_match       = spi_geni_ids,
+       .ops            = &geni_spi_ops,
+       .priv_auto      = sizeof(struct geni_spi_priv),
+       .probe          = geni_spi_probe,
+};
-- 
Sent by a computer, using git, on the internet

Reply via email to