[PATCH v2 04/15] spi: Add Qualcomm GENI SPI driver

Stephen Boyd swboyd at chromium.org
Wed Feb 26 23:15:50 CET 2025


Add a SPI driver for Qualcomm's GENI hardware.

Signed-off-by: Stephen Boyd <swboyd at 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



More information about the U-Boot mailing list