[PATCH 01/13] spi: add core support for controllers with offload capabilities
From: David Lechner
Date: Wed Jan 10 2024 - 14:51:38 EST
This adds a feature for specialized SPI controllers that can record
a series of SPI transfers, including tx data, cs assertions, delays,
etc. and then play them back using a hardware trigger without CPU
intervention.
The intended use case for this is with the AXI SPI Engine to capture
data from ADCs at high rates (MSPS) with a stable sample period.
Most of the implementation is controller-specific and will be handled by
drivers that implement the offload_ops callbacks. The API follows a
prepare/enable pattern that should be familiar to users of the clk
subsystem.
Consumers of this API will make calls similar to this:
/* in probe() */
offload = spi_offload_get(spi, 0);
...
/* in some setup function */
ret = spi_offload_prepare(offload, xfers, ARRAY_SIZE(xfers));
...
/* in some enable function */
ret = spi_offload_enable(offload);
...
/* in corresponding disable function */
spi_offload_disable(offload);
...
/* in corresponding teardown function */
spi_offload_unprepare(offload);
...
Signed-off-by: David Lechner <dlechner@xxxxxxxxxxxx>
---
drivers/spi/spi.c | 39 +++++++++++++++
include/linux/spi/spi.h | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 162 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index a4b8c07c5951..f1d66b5d5491 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3057,6 +3057,13 @@ static int spi_controller_check_ops(struct spi_controller *ctlr)
}
}
+ if (ctlr->offload_ops && !(ctlr->offload_ops->get &&
+ ctlr->offload_ops->prepare &&
+ ctlr->offload_ops->unprepare &&
+ ctlr->offload_ops->enable &&
+ ctlr->offload_ops->disable))
+ return -EINVAL;
+
return 0;
}
@@ -4448,6 +4455,38 @@ int spi_write_then_read(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(spi_write_then_read);
+/**
+ * spi_offload_prepare - prepare offload hardware for a transfer
+ * @offload: The offload instance.
+ * @spi: The spi device to use for the transfers.
+ * @xfers: The transfers to be executed.
+ * @num_xfers: The number of transfers.
+ *
+ * Records a series of transfers to be executed later by the offload hardware
+ * trigger.
+ *
+ * Return: 0 on success, else a negative error code.
+ */
+int spi_offload_prepare(struct spi_offload *offload, struct spi_device *spi,
+ struct spi_transfer *xfers, unsigned int num_xfers)
+{
+ struct spi_controller *ctlr = offload->controller;
+ struct spi_message msg;
+ int ret;
+
+ spi_message_init_with_transfers(&msg, xfers, num_xfers);
+
+ ret = __spi_validate(spi, &msg);
+ if (ret)
+ return ret;
+
+ msg.spi = spi;
+ ret = ctlr->offload_ops->prepare(offload, &msg);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_offload_prepare);
+
/*-------------------------------------------------------------------------*/
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 5d65a6273dcf..f116dfc1d52c 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -28,6 +28,8 @@ struct spi_transfer;
struct spi_controller_mem_ops;
struct spi_controller_mem_caps;
struct spi_message;
+struct spi_controller_offload_ops;
+struct spi_offload;
/*
* INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
@@ -713,6 +715,9 @@ struct spi_controller {
const struct spi_controller_mem_ops *mem_ops;
const struct spi_controller_mem_caps *mem_caps;
+ /* Operations for controllers with offload support. */
+ const struct spi_controller_offload_ops *offload_ops;
+
/* GPIO chip select */
struct gpio_desc **cs_gpiods;
bool use_gpio_descriptors;
@@ -1505,6 +1510,124 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
/*---------------------------------------------------------------------------*/
+/*
+ * Offloading support.
+ *
+ * Some SPI controllers support offloading of SPI transfers. Essentially,
+ * this allows the SPI controller to record SPI transfers and then play them
+ * back later via a hardware trigger.
+ */
+
+/**
+ * SPI_OFFLOAD_RX - placeholder for indicating read transfers for offloads
+ *
+ * Assign xfer->rx_buf to this value for any read transfer passed to
+ * spi_offload_prepare(). This will act as a flag to indicate to the offload
+ * that it should do something with the data read during this transfer. What
+ * that something can be is determined by the specific hardware, e.g. it could
+ * be piped to DMA or a DSP, etc.
+ */
+#define SPI_OFFLOAD_RX_SENTINEL ((void *)1)
+
+/**
+ * struct spi_controller_offload_ops - callbacks for offload support
+ *
+ * Drivers for hardware with offload support need to implement all of these
+ * callbacks.
+ */
+struct spi_controller_offload_ops {
+ /**
+ * @get: Callback to get the offload assigned to the given SPI device.
+ * Index is an index in the offloads array fwnode property of the device.
+ * Implementations must return the pointer to the device or a negative
+ * error code (return -ENODEV rather than NULL if no matching device).
+ */
+ struct spi_offload *(*get)(struct spi_device *spi, unsigned int index);
+ /**
+ * @prepare: Callback to prepare the offload for the given SPI message.
+ * @msg and any of its members (including any xfer->tx_buf) is not
+ * guaranteed to be valid beyond the lifetime of this call.
+ */
+ int (*prepare)(struct spi_offload *offload, struct spi_message *msg);
+ /**
+ * @unprepare: Callback to release any resources used by prepare().
+ */
+ void (*unprepare)(struct spi_offload *offload);
+ /**
+ * @enable: Callback to enable the offload.
+ */
+ int (*enable)(struct spi_offload *offload);
+ /**
+ * @disable: Callback to disable the offload.
+ */
+ void (*disable)(struct spi_offload *offload);
+};
+
+/** struct spi_offload - offload handle */
+struct spi_offload {
+ /** @controller: The associated SPI controller. */
+ struct spi_controller *controller;
+ /** @dev: The device associated with the offload instance. */
+ struct device *dev;
+ /** @priv: Private instance data used by the SPI controller. */
+ void *priv;
+};
+
+/**
+ * spi_offload_get - gets an offload assigned to the given SPI device
+ * @spi: SPI device.
+ * @index: Index of the offload in the SPI device's fwnode int array.
+ *
+ * The lifetime of the returned offload is tied to the struct spi_controller
+ * instance. Since @spi owns a reference to the controller, most consumers
+ * should not have to do anything extra. But if the offload is passed somewhere
+ * outside of the control of the SPI device driver, then an additional reference
+ * to the controller must be made.
+ *
+ * Return: Pointer to the offload handle or negative error code.
+ */
+static inline struct spi_offload *spi_offload_get(struct spi_device *spi,
+ unsigned int index)
+{
+ if (!spi->controller->offload_ops)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ return spi->controller->offload_ops->get(spi, index);
+}
+
+int spi_offload_prepare(struct spi_offload *offload, struct spi_device *spi,
+ struct spi_transfer *xfers, unsigned int num_xfers);
+
+/**
+ * spi_offload_unprepare - releases any resources used by spi_offload_prepare()
+ * @offload: The offload instance.
+ */
+static inline void spi_offload_unprepare(struct spi_offload *offload)
+{
+ offload->controller->offload_ops->unprepare(offload);
+}
+
+/**
+ * spi_offload_enable - enables the offload
+ * @offload: The offload instance.
+ * Return: 0 on success or negative error code.
+ */
+static inline int spi_offload_enable(struct spi_offload *offload)
+{
+ return offload->controller->offload_ops->enable(offload);
+}
+
+/**
+ * spi_offload_disable - disables the offload
+ * @offload: The offload instance.
+ */
+static inline void spi_offload_disable(struct spi_offload *offload)
+{
+ offload->controller->offload_ops->disable(offload);
+}
+
+/*---------------------------------------------------------------------------*/
+
/*
* INTERFACE between board init code and SPI infrastructure.
*
--
2.43.0