[PATCH 1/7] spi: add rockchip spi controller support
Sascha Hauer
s.hauer at pengutronix.de
Tue Oct 15 04:10:58 PDT 2024
This adds support for the SPI controller found on many Rockchip SoCs.
The driver is based on the corresponding Linux driver as of
Linux-6.12-rc1.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
drivers/spi/Makefile | 1 +
drivers/spi/spi-rockchip.c | 584 +++++++++++++++++++++++++++++++++++++++++++++
include/spi/spi.h | 7 +
3 files changed, 592 insertions(+)
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 68a8c4e675..f9aefdfe45 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SPI_ZYNQ_QSPI) += zynq_qspi.o
obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o
obj-$(CONFIG_DRIVER_SPI_STM32) += stm32_spi.o
obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
+obj-y += spi-rockchip.o
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
new file mode 100644
index 0000000000..1e81e9393f
--- /dev/null
+++ b/drivers/spi/spi-rockchip.c
@@ -0,0 +1,584 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Addy Ke <addy.ke at rock-chips.com>
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <driver.h>
+#include <errno.h>
+#include <spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/bitops.h>
+#include <clock.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+#define DRIVER_NAME "rockchip-spi"
+
+#define ROCKCHIP_SPI_CLR_BITS(reg, bits) \
+ writel_relaxed(readl_relaxed(reg) & ~(bits), reg)
+#define ROCKCHIP_SPI_SET_BITS(reg, bits) \
+ writel_relaxed(readl_relaxed(reg) | (bits), reg)
+
+/* SPI register offsets */
+#define ROCKCHIP_SPI_CTRLR0 0x0000
+#define ROCKCHIP_SPI_CTRLR1 0x0004
+#define ROCKCHIP_SPI_SSIENR 0x0008
+#define ROCKCHIP_SPI_SER 0x000c
+#define ROCKCHIP_SPI_BAUDR 0x0010
+#define ROCKCHIP_SPI_TXFTLR 0x0014
+#define ROCKCHIP_SPI_RXFTLR 0x0018
+#define ROCKCHIP_SPI_TXFLR 0x001c
+#define ROCKCHIP_SPI_RXFLR 0x0020
+#define ROCKCHIP_SPI_SR 0x0024
+#define ROCKCHIP_SPI_IPR 0x0028
+#define ROCKCHIP_SPI_IMR 0x002c
+#define ROCKCHIP_SPI_ISR 0x0030
+#define ROCKCHIP_SPI_RISR 0x0034
+#define ROCKCHIP_SPI_ICR 0x0038
+#define ROCKCHIP_SPI_DMACR 0x003c
+#define ROCKCHIP_SPI_DMATDLR 0x0040
+#define ROCKCHIP_SPI_DMARDLR 0x0044
+#define ROCKCHIP_SPI_VERSION 0x0048
+#define ROCKCHIP_SPI_TXDR 0x0400
+#define ROCKCHIP_SPI_RXDR 0x0800
+
+/* Bit fields in CTRLR0 */
+#define CR0_DFS_OFFSET 0
+#define CR0_DFS_4BIT 0x0
+#define CR0_DFS_8BIT 0x1
+#define CR0_DFS_16BIT 0x2
+
+#define CR0_CFS_OFFSET 2
+
+#define CR0_SCPH_OFFSET 6
+
+#define CR0_SCPOL_OFFSET 7
+
+#define CR0_CSM_OFFSET 8
+#define CR0_CSM_KEEP 0x0
+/* ss_n be high for half sclk_out cycles */
+#define CR0_CSM_HALF 0X1
+/* ss_n be high for one sclk_out cycle */
+#define CR0_CSM_ONE 0x2
+
+/* ss_n to sclk_out delay */
+#define CR0_SSD_OFFSET 10
+/*
+ * The period between ss_n active and
+ * sclk_out active is half sclk_out cycles
+ */
+#define CR0_SSD_HALF 0x0
+/*
+ * The period between ss_n active and
+ * sclk_out active is one sclk_out cycle
+ */
+#define CR0_SSD_ONE 0x1
+
+#define CR0_EM_OFFSET 11
+#define CR0_EM_LITTLE 0x0
+#define CR0_EM_BIG 0x1
+
+#define CR0_FBM_OFFSET 12
+#define CR0_FBM_MSB 0x0
+#define CR0_FBM_LSB 0x1
+
+#define CR0_BHT_OFFSET 13
+#define CR0_BHT_16BIT 0x0
+#define CR0_BHT_8BIT 0x1
+
+#define CR0_RSD_OFFSET 14
+#define CR0_RSD_MAX 0x3
+
+#define CR0_FRF_OFFSET 16
+#define CR0_FRF_SPI 0x0
+#define CR0_FRF_SSP 0x1
+#define CR0_FRF_MICROWIRE 0x2
+
+#define CR0_XFM_OFFSET 18
+#define CR0_XFM_MASK (0x03 << SPI_XFM_OFFSET)
+#define CR0_XFM_TR 0x0
+#define CR0_XFM_TO 0x1
+#define CR0_XFM_RO 0x2
+
+#define CR0_OPM_OFFSET 20
+#define CR0_OPM_HOST 0x0
+#define CR0_OPM_TARGET 0x1
+
+#define CR0_SOI_OFFSET 23
+
+#define CR0_MTM_OFFSET 0x21
+
+/* Bit fields in SER, 2bit */
+#define SER_MASK 0x3
+
+/* Bit fields in BAUDR */
+#define BAUDR_SCKDV_MIN 2
+#define BAUDR_SCKDV_MAX 65534
+
+/* Bit fields in SR, 6bit */
+#define SR_MASK 0x3f
+#define SR_BUSY (1 << 0)
+#define SR_TF_FULL (1 << 1)
+#define SR_TF_EMPTY (1 << 2)
+#define SR_RF_EMPTY (1 << 3)
+#define SR_RF_FULL (1 << 4)
+#define SR_TARGET_TX_BUSY (1 << 5)
+
+/* Bit fields in ISR, IMR, ISR, RISR, 5bit */
+#define INT_MASK 0x1f
+#define INT_TF_EMPTY (1 << 0)
+#define INT_TF_OVERFLOW (1 << 1)
+#define INT_RF_UNDERFLOW (1 << 2)
+#define INT_RF_OVERFLOW (1 << 3)
+#define INT_RF_FULL (1 << 4)
+#define INT_CS_INACTIVE (1 << 6)
+
+/* Bit fields in ICR, 4bit */
+#define ICR_MASK 0x0f
+#define ICR_ALL (1 << 0)
+#define ICR_RF_UNDERFLOW (1 << 1)
+#define ICR_RF_OVERFLOW (1 << 2)
+#define ICR_TF_OVERFLOW (1 << 3)
+
+/* Bit fields in DMACR */
+#define RF_DMA_EN (1 << 0)
+#define TF_DMA_EN (1 << 1)
+
+/* Driver state flags */
+#define RXDMA (1 << 0)
+#define TXDMA (1 << 1)
+
+/* sclk_out: spi host internal logic in rk3x can support 50Mhz */
+#define MAX_SCLK_OUT 50000000U
+
+/*
+ * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However,
+ * the controller seems to hang when given 0x10000, so stick with this for now.
+ */
+#define ROCKCHIP_SPI_MAX_TRANLEN 0xffff
+
+#define ROCKCHIP_SPI_MAX_NATIVE_CS_NUM 2
+#define ROCKCHIP_SPI_VER2_TYPE1 0x05EC0002
+#define ROCKCHIP_SPI_VER2_TYPE2 0x00110002
+
+#define ROCKCHIP_AUTOSUSPEND_TIMEOUT 2000
+
+struct rockchip_spi {
+ struct spi_controller ctlr;
+ struct device *dev;
+
+ struct clk *spiclk;
+ struct clk *apb_pclk;
+
+ void __iomem *regs;
+
+ /*depth of the FIFO buffer */
+ u32 fifo_len;
+ /* frequency of spiclk */
+ u32 freq;
+
+ u8 rsd;
+
+ bool cs_high_supported; /* native CS supports active-high polarity */
+
+ struct spi_transfer *xfer; /* Store xfer temporarily */
+};
+
+static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
+{
+ writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR);
+}
+
+static inline void wait_for_tx_idle(struct rockchip_spi *rs, bool target_mode)
+{
+ u64 start = get_time_ns();
+
+ do {
+ if (target_mode) {
+ if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_TARGET_TX_BUSY) &&
+ !((readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY)))
+ return;
+ } else {
+ if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
+ return;
+ }
+ } while (!is_timeout(start, 5 * MSECOND));
+
+ dev_warn(rs->dev, "spi controller is in busy state!\n");
+}
+
+static u32 get_fifo_len(struct rockchip_spi *rs)
+{
+ u32 ver;
+
+ ver = readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION);
+
+ switch (ver) {
+ case ROCKCHIP_SPI_VER2_TYPE1:
+ case ROCKCHIP_SPI_VER2_TYPE2:
+ return 64;
+ default:
+ return 32;
+ }
+}
+
+static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx)
+{
+ return NULL;
+}
+
+static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
+{
+ struct spi_controller *ctlr = spi->controller;
+ struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ bool cs_asserted = spi->mode & SPI_CS_HIGH ? !enable : enable;
+
+ if (cs_asserted) {
+ if (spi_get_csgpiod(spi, 0))
+ ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
+ else
+ ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER,
+ BIT(spi_get_chipselect(spi, 0)));
+ } else {
+ if (spi_get_csgpiod(spi, 0))
+ ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
+ else
+ ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER,
+ BIT(spi_get_chipselect(spi, 0)));
+ }
+}
+
+static int rockchip_spi_pio(struct rockchip_spi *rs, struct spi_transfer *xfer)
+{
+ int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2;
+ const void *txbuf = xfer->tx_buf;
+ void *rxbuf = xfer->rx_buf;
+ int rxwords, txwords;
+
+ txwords = rxwords = xfer->len / bytes_per_word;
+
+ while (1) {
+ unsigned int fifowords = readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFLR);
+ unsigned int tx_free = rs->fifo_len - fifowords;
+
+ if (tx_free && txwords) {
+ u32 txw;
+
+ if (!txbuf)
+ txw = 0;
+ else if (bytes_per_word == 1)
+ txw = *(u8 *)txbuf;
+ else
+ txw = *(u16 *)txbuf;
+
+ writel_relaxed(txw, rs->regs + ROCKCHIP_SPI_TXDR);
+
+ if (txbuf)
+ txbuf += bytes_per_word;
+
+ txwords--;
+ }
+
+ fifowords = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+
+ if (fifowords) {
+ u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+
+ if (rxbuf) {
+ if (bytes_per_word == 1)
+ *(u8 *)rxbuf = (u8)rxw;
+ else
+ *(u16 *)rxbuf = (u16)rxw;
+ rxbuf += bytes_per_word;
+ }
+
+ rxwords--;
+ }
+
+ if (!rxwords)
+ return 0;
+ }
+}
+
+static int rockchip_spi_config(struct rockchip_spi *rs,
+ struct spi_device *spi, struct spi_transfer *xfer)
+{
+ u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET
+ | CR0_BHT_8BIT << CR0_BHT_OFFSET
+ | CR0_SSD_ONE << CR0_SSD_OFFSET
+ | CR0_EM_BIG << CR0_EM_OFFSET;
+
+ cr0 |= rs->rsd << CR0_RSD_OFFSET;
+ cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
+ if (spi->mode & SPI_LSB_FIRST)
+ cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET;
+ if (spi->mode & SPI_CS_HIGH)
+ cr0 |= BIT(spi_get_chipselect(spi, 0)) << CR0_SOI_OFFSET;
+
+ if (xfer->rx_buf && xfer->tx_buf)
+ cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
+ else if (xfer->rx_buf)
+ cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET;
+
+ cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
+
+ switch (xfer->bits_per_word) {
+ case 4:
+ cr0 |= CR0_DFS_4BIT << CR0_DFS_OFFSET;
+ break;
+ case 8:
+ cr0 |= CR0_DFS_8BIT << CR0_DFS_OFFSET;
+ break;
+ case 16:
+ cr0 |= CR0_DFS_16BIT << CR0_DFS_OFFSET;
+ break;
+ default:
+ /* we only whitelist 4, 8 and 16 bit words in
+ * ctlr->bits_per_word_mask, so this shouldn't
+ * happen
+ */
+ dev_err(rs->dev, "unknown bits per word: %d\n",
+ xfer->bits_per_word);
+ return -EINVAL;
+ }
+
+ writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+ /* the hardware only supports an even clock divisor, so
+ * round divisor = spiclk / speed up to nearest even number
+ * so that the resulting speed is <= the requested speed
+ */
+ writel_relaxed(2 * DIV_ROUND_UP(rs->freq, 2 * xfer->speed_hz),
+ rs->regs + ROCKCHIP_SPI_BAUDR);
+
+ return 0;
+}
+
+static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
+{
+ return ROCKCHIP_SPI_MAX_TRANLEN;
+}
+
+static int rockchip_spi_transfer_one(
+ struct rockchip_spi *rs,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ int ret;
+
+ /* Zero length transfers won't trigger an interrupt on completion */
+ if (!xfer->len)
+ return 1;
+
+ if (!xfer->tx_buf && !xfer->rx_buf) {
+ dev_err(rs->dev, "No buffer for transfer\n");
+ return -EINVAL;
+ }
+
+ if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {
+ dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);
+ return -EINVAL;
+ }
+
+ ret = rockchip_spi_config(rs, spi, xfer);
+ if (ret)
+ return ret;
+
+ spi_enable_chip(rs, true);
+ rockchip_spi_pio(rs, xfer);
+ spi_enable_chip(rs, false);
+
+ return 0;
+}
+
+static int rockchip_spi_transfer(struct spi_device *spi_dev, struct spi_message *msg)
+{
+ struct rockchip_spi *rs = spi_controller_get_devdata(spi_dev->controller);
+ struct spi_transfer *t;
+ int ret = 0;
+
+ if (list_empty(&msg->transfers))
+ return 0;
+
+ rockchip_spi_set_cs(spi_dev, true);
+
+ msg->actual_length = 0;
+
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+ dev_dbg(rs->dev, " xfer %p: len %u tx %p rx %p\n",
+ t, t->len, t->tx_buf, t->rx_buf);
+
+ ret = rockchip_spi_transfer_one(rs, spi_dev, t);
+ if (ret < 0)
+ goto out;
+ msg->actual_length += t->len;
+ }
+
+out:
+ rockchip_spi_set_cs(spi_dev, false);
+ return ret;
+}
+
+static int rockchip_spi_setup(struct spi_device *spi)
+{
+ struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
+ u32 cr0;
+
+ if (!spi_get_csgpiod(spi, 0) && (spi->mode & SPI_CS_HIGH) && !rs->cs_high_supported) {
+ dev_warn(&spi->dev, "setup: non GPIO CS can't be active-high\n");
+ return -EINVAL;
+ }
+
+ cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+ cr0 &= ~(0x3 << CR0_SCPH_OFFSET);
+ cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET);
+ if (spi->mode & SPI_CS_HIGH && spi_get_chipselect(spi, 0) <= 1)
+ cr0 |= BIT(spi_get_chipselect(spi, 0)) << CR0_SOI_OFFSET;
+ else if (spi_get_chipselect(spi, 0) <= 1)
+ cr0 &= ~(BIT(spi_get_chipselect(spi, 0)) << CR0_SOI_OFFSET);
+
+ writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+ return 0;
+}
+
+static int rockchip_spi_probe(struct device *dev)
+{
+ int ret;
+ struct rockchip_spi *rs;
+ struct spi_controller *ctlr;
+ struct device_node *np = dev->of_node;
+ u32 rsd_nsecs, num_cs;
+ bool target_mode;
+ struct resource *iores;
+
+ target_mode = of_property_read_bool(np, "spi-slave");
+ if (target_mode)
+ return 0;
+
+ rs = xzalloc(sizeof(*rs));
+ ctlr = &rs->ctlr;
+
+ /* Get basic io resource and map it */
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ rs->regs = IOMEM(iores->start);
+
+ rs->apb_pclk = clk_get_enabled(dev, "apb_pclk");
+ if (IS_ERR(rs->apb_pclk)) {
+ dev_err(dev, "Failed to get apb_pclk\n");
+ ret = PTR_ERR(rs->apb_pclk);
+ goto err_put_ctlr;
+ }
+
+ rs->spiclk = clk_get_enabled(dev, "spiclk");
+ if (IS_ERR(rs->spiclk)) {
+ dev_err(dev, "Failed to get spi_pclk\n");
+ ret = PTR_ERR(rs->spiclk);
+ goto err_put_ctlr;
+ }
+
+ spi_enable_chip(rs, false);
+
+ rs->dev = dev;
+ rs->freq = clk_get_rate(rs->spiclk);
+
+ if (!of_property_read_u32(np, "rx-sample-delay-ns",
+ &rsd_nsecs)) {
+ /* rx sample delay is expressed in parent clock cycles (max 3) */
+ u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8),
+ 1000000000 >> 8);
+ if (!rsd) {
+ dev_warn(rs->dev, "%u Hz are too slow to express %u ns delay\n",
+ rs->freq, rsd_nsecs);
+ } else if (rsd > CR0_RSD_MAX) {
+ rsd = CR0_RSD_MAX;
+ dev_warn(rs->dev, "%u Hz are too fast to express %u ns delay, clamping at %u ns\n",
+ rs->freq, rsd_nsecs,
+ CR0_RSD_MAX * 1000000000U / rs->freq);
+ }
+ rs->rsd = rsd;
+ }
+
+ rs->fifo_len = get_fifo_len(rs);
+ if (!rs->fifo_len) {
+ dev_err(dev, "Failed to get fifo length\n");
+ ret = -EINVAL;
+ goto err_put_ctlr;
+ }
+
+// ctlr->bus_num = pdev->id;
+// ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
+
+ /*
+ * rk spi0 has two native cs, spi1..5 one cs only
+ * if num-cs is missing in the dts, default to 1
+ */
+ num_cs = 1;
+ of_property_read_u32(np, "num-cs", &num_cs);
+ ctlr->num_chipselect = num_cs;
+
+ ctlr->dev = dev;
+ ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4);
+ ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
+
+ ctlr->setup = rockchip_spi_setup;
+ ctlr->transfer = rockchip_spi_transfer;
+ ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
+
+ switch (readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION)) {
+ case ROCKCHIP_SPI_VER2_TYPE2:
+ rs->cs_high_supported = true;
+ break;
+ }
+
+ spi_controller_set_devdata(ctlr, rs);
+
+ ret = spi_register_controller(ctlr);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register controller\n");
+ goto err_free_dma_rx;
+ }
+
+ return 0;
+
+err_free_dma_rx:
+err_put_ctlr:
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_spi_dt_match[] = {
+ { .compatible = "rockchip,px30-spi", },
+ { .compatible = "rockchip,rk3036-spi", },
+ { .compatible = "rockchip,rk3066-spi", },
+ { .compatible = "rockchip,rk3188-spi", },
+ { .compatible = "rockchip,rk3228-spi", },
+ { .compatible = "rockchip,rk3288-spi", },
+ { .compatible = "rockchip,rk3308-spi", },
+ { .compatible = "rockchip,rk3328-spi", },
+ { .compatible = "rockchip,rk3368-spi", },
+ { .compatible = "rockchip,rk3399-spi", },
+ { .compatible = "rockchip,rv1108-spi", },
+ { .compatible = "rockchip,rv1126-spi", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
+
+static struct driver rockchip_spi_driver = {
+ .name = DRIVER_NAME,
+ .probe = rockchip_spi_probe,
+ .of_compatible = rockchip_spi_dt_match,
+};
+coredevice_platform_driver(rockchip_spi_driver);
+
+MODULE_AUTHOR("Addy Ke <addy.ke at rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP SPI Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 45d6f5931c..0c60e99868 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -115,6 +115,13 @@ static inline struct spi_device *to_spi_device(struct device *dev)
return dev ? container_of(dev, struct spi_device, dev) : NULL;
}
+static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
+{
+ BUG_ON(idx != 0);
+
+ return spi->chip_select;
+}
+
struct spi_message;
/**
--
2.39.5
More information about the barebox
mailing list