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