diff mbox series

[v5,06/16] spi: add offload TX/RX streaming APIs

Message ID 20241115-dlech-mainline-spi-engine-offload-2-v5-6-bea815bd5ea5@baylibre.com
State New
Headers show
Series spi: axi-spi-engine: add offload support | expand

Commit Message

David Lechner Nov. 15, 2024, 8:18 p.m. UTC
Most configuration of SPI offloads is handled opaquely using the offload
pointer that is passed to the various offload functions. However, there
are some offload features that need to be controlled on a per transfer
basis.

This patch adds a flag field to struct spi_transfer to allow specifying
such features. The first feature to be added is the ability to stream
data to/from a hardware sink/source rather than using a tx or rx buffer.
Additional flags can be added in the future as needed.

A flags field is also added to the offload struct for providers to
indicate which flags are supported. This allows for generic checking of
offload capabilities during __spi_validate() so that each offload
provider doesn't have to implement their own validation.

As a first users of this streaming capability, getter functions are
added to get a DMA channel that is directly connected to the offload.
Peripheral drivers will use this to get a DMA channel and configure it
to suit their needs.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---

v5 change:
* Remove incorrect comment about caller needing to release DMA channels.

v4 changes:
* DMA API's now automatically release DMA channels instead of leaving
  it up to the caller.

v3 changes:
* Added spi_offload_{tx,rx}_stream_get_dma_chan() functions.

v2 changes:
* This is also split out from "spi: add core support for controllers with
  offload capabilities".
* In the previous version, we were using (void *)-1 as a sentinel value
  that could be assigned, e.g. to rx_buf. But this was naive since there
  is core code that would try to dereference this pointer. So instead,
  we've added a new flags field to the spi_transfer structure for this
  sort of thing. This also has the advantage of being able to be used in
  the future for other arbitrary features.
---
 drivers/spi/spi-offload.c       | 70 +++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi.c               | 10 ++++++
 include/linux/spi/spi-offload.h | 24 ++++++++++++++
 include/linux/spi/spi.h         |  3 ++
 4 files changed, 107 insertions(+)

Comments

Jonathan Cameron Nov. 24, 2024, 4:50 p.m. UTC | #1
On Fri, 15 Nov 2024 14:18:45 -0600
David Lechner <dlechner@baylibre.com> wrote:

> Most configuration of SPI offloads is handled opaquely using the offload
> pointer that is passed to the various offload functions. However, there
> are some offload features that need to be controlled on a per transfer
> basis.
> 
> This patch adds a flag field to struct spi_transfer to allow specifying
> such features. The first feature to be added is the ability to stream
> data to/from a hardware sink/source rather than using a tx or rx buffer.
> Additional flags can be added in the future as needed.
> 
> A flags field is also added to the offload struct for providers to
> indicate which flags are supported. This allows for generic checking of
> offload capabilities during __spi_validate() so that each offload
> provider doesn't have to implement their own validation.
> 
> As a first users of this streaming capability, getter functions are
> added to get a DMA channel that is directly connected to the offload.
> Peripheral drivers will use this to get a DMA channel and configure it
> to suit their needs.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Some docs that need updating.  Otherwise I wonder if we should delay
the _tx variants until there is a driver using them.

I'm sure you have one on the way and there is an argument that it makes
sense to review rx and tx together, but still good to only add code
when it's used.

Jonathan

> ---
> 
> v5 change:
> * Remove incorrect comment about caller needing to release DMA channels.
> 
> v4 changes:
> * DMA API's now automatically release DMA channels instead of leaving
>   it up to the caller.
> 
> v3 changes:
> * Added spi_offload_{tx,rx}_stream_get_dma_chan() functions.
> 
> v2 changes:
> * This is also split out from "spi: add core support for controllers with
>   offload capabilities".
> * In the previous version, we were using (void *)-1 as a sentinel value
>   that could be assigned, e.g. to rx_buf. But this was naive since there
>   is core code that would try to dereference this pointer. So instead,
>   we've added a new flags field to the spi_transfer structure for this
>   sort of thing. This also has the advantage of being able to be used in
>   the future for other arbitrary features.
> ---
>  drivers/spi/spi-offload.c       | 70 +++++++++++++++++++++++++++++++++++++++++
>  drivers/spi/spi.c               | 10 ++++++
>  include/linux/spi/spi-offload.h | 24 ++++++++++++++
>  include/linux/spi/spi.h         |  3 ++
>  4 files changed, 107 insertions(+)
> 
> diff --git a/drivers/spi/spi-offload.c b/drivers/spi/spi-offload.c
> index 01d7b632d109..7b3e20ad9e4f 100644
> --- a/drivers/spi/spi-offload.c
> +++ b/drivers/spi/spi-offload.c
> @@ -8,6 +8,7 @@
>  
>  #include <linux/cleanup.h>
>  #include <linux/device.h>
> +#include <linux/dmaengine.h>
>  #include <linux/export.h>
>  #include <linux/kref.h>
>  #include <linux/list.h>
> @@ -319,6 +320,75 @@ void spi_offload_trigger_disable(struct spi_offload *offload,
>  }
>  EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
>  
> +static void spi_offload_release_dma_chan(void *chan)
> +{
> +	dma_release_channel(chan);
> +}
> +
> +/**
> + * spi_offload_tx_stream_request_dma_chan_info - Get the DMA channel info for the TX stream
> + * @spi: SPI device
> + * @id: Function ID if SPI device uses more than one offload or NULL.
As below.  Code is ahead of the docs.
> + *
> + * This is the DMA channel that will provide data to transfers that use the
> + * %SPI_OFFLOAD_XFER_TX_STREAM offload flag.
> + *
> + * Return: Pointer to DMA channel info, or negative error code
> + */
> +struct dma_chan
> +*devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
> +					     struct spi_offload *offload)
> +{
> +	struct dma_chan *chan;
> +	int ret;
> +
> +	if (!offload->ops || !offload->ops->tx_stream_request_dma_chan)
> +		return ERR_PTR(-EOPNOTSUPP);
> +
> +	chan = offload->ops->tx_stream_request_dma_chan(offload);
> +	if (IS_ERR(chan))
> +		return chan;
> +
> +	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	return chan;
> +}
> +EXPORT_SYMBOL_GPL(devm_spi_offload_tx_stream_request_dma_chan);
> +
> +/**
> + * spi_offload_rx_stream_request_dma_chan_info - Get the DMA channel info for the RX stream
> + * @spi: SPI device

Run kernel-doc over these. There is no spi here.


> + * @id: Function ID if SPI device uses more than one offload or NULL.
or indeed ID...
> + *
> + * This is the DMA channel that will receive data from transfers that use the
> + * %SPI_OFFLOAD_XFER_RX_STREAM offload flag.
> + *
> + * Return: Pointer to DMA channel info, or negative error code
> + */
> +struct dma_chan
> +*devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
> +					     struct spi_offload *offload)
> +{
> +	struct dma_chan *chan;
> +	int ret;
> +
> +	if (!offload->ops || !offload->ops->rx_stream_request_dma_chan)
> +		return ERR_PTR(-EOPNOTSUPP);
> +
> +	chan = offload->ops->rx_stream_request_dma_chan(offload);
> +	if (IS_ERR(chan))
> +		return chan;
> +
> +	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	return chan;
> +}
> +EXPORT_SYMBOL_GPL(devm_spi_offload_rx_stream_request_dma_chan);
> +
>  /* Triggers providers */
>  
>  static void spi_offload_trigger_unregister(void *data)

>  
>  struct spi_offload *devm_spi_offload_alloc(struct device *dev, size_t priv_size);
> @@ -107,6 +126,11 @@ int spi_offload_trigger_enable(struct spi_offload *offload,
>  void spi_offload_trigger_disable(struct spi_offload *offload,
>  				 struct spi_offload_trigger *trigger);
>  
> +struct dma_chan *devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
> +							     struct spi_offload *offload);
Would be better supported by actual driver code using it.  Maybe it is better to bring
in TX only with the first user?


> +struct dma_chan *devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
> +							     struct spi_offload *offload);
> +
>  /* Trigger providers */
David Lechner Nov. 24, 2024, 5:54 p.m. UTC | #2
On 11/24/24 10:50 AM, Jonathan Cameron wrote:
> On Fri, 15 Nov 2024 14:18:45 -0600
> David Lechner <dlechner@baylibre.com> wrote:
> 
>> Most configuration of SPI offloads is handled opaquely using the offload
>> pointer that is passed to the various offload functions. However, there
>> are some offload features that need to be controlled on a per transfer
>> basis.
>>
>> This patch adds a flag field to struct spi_transfer to allow specifying
>> such features. The first feature to be added is the ability to stream
>> data to/from a hardware sink/source rather than using a tx or rx buffer.
>> Additional flags can be added in the future as needed.
>>
>> A flags field is also added to the offload struct for providers to
>> indicate which flags are supported. This allows for generic checking of
>> offload capabilities during __spi_validate() so that each offload
>> provider doesn't have to implement their own validation.
>>
>> As a first users of this streaming capability, getter functions are
>> added to get a DMA channel that is directly connected to the offload.
>> Peripheral drivers will use this to get a DMA channel and configure it
>> to suit their needs.
>>
>> Signed-off-by: David Lechner <dlechner@baylibre.com>
> Some docs that need updating.  Otherwise I wonder if we should delay
> the _tx variants until there is a driver using them.
> 
> I'm sure you have one on the way and there is an argument that it makes
> sense to review rx and tx together, but still good to only add code
> when it's used.
> 
> Jonathan
> 

In v1 Mark commented that he expected TX along with RX. And we do
have a DAC driver we can probably add to the series in the next
revision that uses it.
Jonathan Cameron Nov. 25, 2024, 9:30 p.m. UTC | #3
On Sun, 24 Nov 2024 11:54:44 -0600
David Lechner <dlechner@baylibre.com> wrote:

> On 11/24/24 10:50 AM, Jonathan Cameron wrote:
> > On Fri, 15 Nov 2024 14:18:45 -0600
> > David Lechner <dlechner@baylibre.com> wrote:
> >   
> >> Most configuration of SPI offloads is handled opaquely using the offload
> >> pointer that is passed to the various offload functions. However, there
> >> are some offload features that need to be controlled on a per transfer
> >> basis.
> >>
> >> This patch adds a flag field to struct spi_transfer to allow specifying
> >> such features. The first feature to be added is the ability to stream
> >> data to/from a hardware sink/source rather than using a tx or rx buffer.
> >> Additional flags can be added in the future as needed.
> >>
> >> A flags field is also added to the offload struct for providers to
> >> indicate which flags are supported. This allows for generic checking of
> >> offload capabilities during __spi_validate() so that each offload
> >> provider doesn't have to implement their own validation.
> >>
> >> As a first users of this streaming capability, getter functions are
> >> added to get a DMA channel that is directly connected to the offload.
> >> Peripheral drivers will use this to get a DMA channel and configure it
> >> to suit their needs.
> >>
> >> Signed-off-by: David Lechner <dlechner@baylibre.com>  
> > Some docs that need updating.  Otherwise I wonder if we should delay
> > the _tx variants until there is a driver using them.
> > 
> > I'm sure you have one on the way and there is an argument that it makes
> > sense to review rx and tx together, but still good to only add code
> > when it's used.
> > 
> > Jonathan
> >   
> 
> In v1 Mark commented that he expected TX along with RX. And we do
> have a DAC driver we can probably add to the series in the next
> revision that uses it.
Perfect, or maybe just reference that in the description as
'coming soon'.  I'm fine with infrastructure coming a bit before the
user and I can always trust you to send me more drivers :)

Jonathan
diff mbox series

Patch

diff --git a/drivers/spi/spi-offload.c b/drivers/spi/spi-offload.c
index 01d7b632d109..7b3e20ad9e4f 100644
--- a/drivers/spi/spi-offload.c
+++ b/drivers/spi/spi-offload.c
@@ -8,6 +8,7 @@ 
 
 #include <linux/cleanup.h>
 #include <linux/device.h>
+#include <linux/dmaengine.h>
 #include <linux/export.h>
 #include <linux/kref.h>
 #include <linux/list.h>
@@ -319,6 +320,75 @@  void spi_offload_trigger_disable(struct spi_offload *offload,
 }
 EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
 
+static void spi_offload_release_dma_chan(void *chan)
+{
+	dma_release_channel(chan);
+}
+
+/**
+ * spi_offload_tx_stream_request_dma_chan_info - Get the DMA channel info for the TX stream
+ * @spi: SPI device
+ * @id: Function ID if SPI device uses more than one offload or NULL.
+ *
+ * This is the DMA channel that will provide data to transfers that use the
+ * %SPI_OFFLOAD_XFER_TX_STREAM offload flag.
+ *
+ * Return: Pointer to DMA channel info, or negative error code
+ */
+struct dma_chan
+*devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
+					     struct spi_offload *offload)
+{
+	struct dma_chan *chan;
+	int ret;
+
+	if (!offload->ops || !offload->ops->tx_stream_request_dma_chan)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	chan = offload->ops->tx_stream_request_dma_chan(offload);
+	if (IS_ERR(chan))
+		return chan;
+
+	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return chan;
+}
+EXPORT_SYMBOL_GPL(devm_spi_offload_tx_stream_request_dma_chan);
+
+/**
+ * spi_offload_rx_stream_request_dma_chan_info - Get the DMA channel info for the RX stream
+ * @spi: SPI device
+ * @id: Function ID if SPI device uses more than one offload or NULL.
+ *
+ * This is the DMA channel that will receive data from transfers that use the
+ * %SPI_OFFLOAD_XFER_RX_STREAM offload flag.
+ *
+ * Return: Pointer to DMA channel info, or negative error code
+ */
+struct dma_chan
+*devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
+					     struct spi_offload *offload)
+{
+	struct dma_chan *chan;
+	int ret;
+
+	if (!offload->ops || !offload->ops->rx_stream_request_dma_chan)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	chan = offload->ops->rx_stream_request_dma_chan(offload);
+	if (IS_ERR(chan))
+		return chan;
+
+	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return chan;
+}
+EXPORT_SYMBOL_GPL(devm_spi_offload_rx_stream_request_dma_chan);
+
 /* Triggers providers */
 
 static void spi_offload_trigger_unregister(void *data)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 74e04a4b0f19..47020bd033bd 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -31,6 +31,7 @@ 
 #include <linux/ptp_clock_kernel.h>
 #include <linux/sched/rt.h>
 #include <linux/slab.h>
+#include <linux/spi/spi-offload.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 #include <uapi/linux/sched/types.h>
@@ -4159,6 +4160,15 @@  static int __spi_validate(struct spi_device *spi, struct spi_message *message)
 
 		if (_spi_xfer_word_delay_update(xfer, spi))
 			return -EINVAL;
+
+		/* make sure controller supports required offload features */
+		if (xfer->offload_flags) {
+			if (!message->offload)
+				return -EINVAL;
+
+			if (xfer->offload_flags & ~message->offload->xfer_flags)
+				return -EINVAL;
+		}
 	}
 
 	message->status = -EINPROGRESS;
diff --git a/include/linux/spi/spi-offload.h b/include/linux/spi/spi-offload.h
index c8c673784e99..132b2f819b91 100644
--- a/include/linux/spi/spi-offload.h
+++ b/include/linux/spi/spi-offload.h
@@ -25,6 +25,11 @@  struct device;
 struct fwnode_handle;
 struct spi_device;
 
+/* This is write xfer but TX uses external data stream rather than tx_buf. */
+#define SPI_OFFLOAD_XFER_TX_STREAM	BIT(0)
+/* This is read xfer but RX uses external data stream rather than rx_buf. */
+#define SPI_OFFLOAD_XFER_RX_STREAM	BIT(1)
+
 /* Offload can be triggered by external hardware event. */
 #define SPI_OFFLOAD_CAP_TRIGGER			BIT(0)
 /* Offload can record and then play back TX data when triggered. */
@@ -54,6 +59,8 @@  struct spi_offload {
 	void *priv;
 	/** @ops: callbacks for offload support */
 	const struct spi_offload_ops *ops;
+	/** @xfer_flags: %SPI_OFFLOAD_XFER_* flags supported by provider */
+	u32 xfer_flags;
 };
 
 enum spi_offload_trigger_type {
@@ -89,6 +96,18 @@  struct spi_offload_ops {
 	 * given offload instance.
 	 */
 	void (*trigger_disable)(struct spi_offload *offload);
+	/**
+	 * @tx_stream_request_dma_chan: Optional callback for controllers that
+	 * have an offload where the TX data stream is connected directly to a
+	 * DMA channel.
+	 */
+	struct dma_chan *(*tx_stream_request_dma_chan)(struct spi_offload *offload);
+	/**
+	 * @rx_stream_request_dma_chan: Optional callback for controllers that
+	 * have an offload where the RX data stream is connected directly to a
+	 * DMA channel.
+	 */
+	struct dma_chan *(*rx_stream_request_dma_chan)(struct spi_offload *offload);
 };
 
 struct spi_offload *devm_spi_offload_alloc(struct device *dev, size_t priv_size);
@@ -107,6 +126,11 @@  int spi_offload_trigger_enable(struct spi_offload *offload,
 void spi_offload_trigger_disable(struct spi_offload *offload,
 				 struct spi_offload_trigger *trigger);
 
+struct dma_chan *devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
+							     struct spi_offload *offload);
+struct dma_chan *devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
+							     struct spi_offload *offload);
+
 /* Trigger providers */
 
 struct spi_offload_trigger;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index c2b24a0909ea..e7e612e30a2f 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -1094,6 +1094,9 @@  struct spi_transfer {
 
 	u32		effective_speed_hz;
 
+	/* Use %SPI_OFFLOAD_XFER_* from spi-offload.h */
+	unsigned int	offload_flags;
+
 	unsigned int	ptp_sts_word_pre;
 	unsigned int	ptp_sts_word_post;