[PATCH 01/13] spi: add core support for controllers with offload capabilities

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux