linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] dmaengine: sun4i: Set the maximum segment size
@ 2022-06-21  3:13 Samuel Holland
  2022-07-01 16:28 ` Vinod Koul
  2022-07-05 13:04 ` Vinod Koul
  0 siblings, 2 replies; 4+ messages in thread
From: Samuel Holland @ 2022-06-21  3:13 UTC (permalink / raw)
  To: Chen-Yu Tsai, Jernej Skrabec, Vinod Koul
  Cc: Maxime Ripard, Samuel Holland, dmaengine, linux-arm-kernel,
	linux-kernel, linux-sunxi

The sun4i DMA engine supports transfer sizes up to 128k for normal DMA
and 16M for dedicated DMA, as documented in the A10 and A20 manuals.

Since this is larger than the default segment size limit (64k), exposing
the real limit reduces the number of transfers needed for a transaction.
However, because the device can only report one segment size limit, we
have to expose the smaller limit from normal DMA.

One complication is that the driver combines pairs of periodic transfers
to reduce programming overhead. This only works when the period size is
at most half of the maximum transfer size. With the default 64k segment
size limit, this was always the case, but for normal DMA it is no longer
guaranteed. Skip the optimization if the period is too long; even
without it, the overhead is less than before.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
I don't have any A10 or A20 boards I can test this on.

 drivers/dma/sun4i-dma.c | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
index 93f1645ae928..f291b1b4db32 100644
--- a/drivers/dma/sun4i-dma.c
+++ b/drivers/dma/sun4i-dma.c
@@ -7,6 +7,7 @@
 #include <linux/bitmap.h>
 #include <linux/bitops.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/dmapool.h>
 #include <linux/interrupt.h>
@@ -122,6 +123,15 @@
 	 SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) |				\
 	 SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
 
+/*
+ * Normal DMA supports individual transfers (segments) up to 128k.
+ * Dedicated DMA supports transfers up to 16M. We can only report
+ * one size limit, so we have to use the smaller value.
+ */
+#define SUN4I_NDMA_MAX_SEG_SIZE		SZ_128K
+#define SUN4I_DDMA_MAX_SEG_SIZE		SZ_16M
+#define SUN4I_DMA_MAX_SEG_SIZE		SUN4I_NDMA_MAX_SEG_SIZE
+
 struct sun4i_dma_pchan {
 	/* Register base of channel */
 	void __iomem			*base;
@@ -155,7 +165,8 @@ struct sun4i_dma_contract {
 	struct virt_dma_desc		vd;
 	struct list_head		demands;
 	struct list_head		completed_demands;
-	int				is_cyclic;
+	bool				is_cyclic : 1;
+	bool				use_half_int : 1;
 };
 
 struct sun4i_dma_dev {
@@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
 	if (promise) {
 		vchan->contract = contract;
 		vchan->pchan = pchan;
-		set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1);
+		set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);
 		configure_pchan(pchan, promise);
 	}
 
@@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
 	 *
 	 * Which requires half the engine programming for the same
 	 * functionality.
+	 *
+	 * This only works if two periods fit in a single promise. That will
+	 * always be the case for dedicated DMA, where the hardware has a much
+	 * larger maximum transfer size than advertised to clients.
 	 */
-	nr_periods = DIV_ROUND_UP(len / period_len, 2);
+	if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
+		period_len *= 2;
+		contract->use_half_int = 1;
+	}
+
+	nr_periods = DIV_ROUND_UP(len, period_len);
 	for (i = 0; i < nr_periods; i++) {
 		/* Calculate the offset in the buffer and the length needed */
-		offset = i * period_len * 2;
-		plength = min((len - offset), (period_len * 2));
+		offset = i * period_len;
+		plength = min((len - offset), period_len);
 		if (dir == DMA_MEM_TO_DEV)
 			src = buf + offset;
 		else
@@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, priv);
 	spin_lock_init(&priv->lock);
 
+	dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
+
 	dma_cap_zero(priv->slave.cap_mask);
 	dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
 	dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
-- 
2.35.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] dmaengine: sun4i: Set the maximum segment size
  2022-06-21  3:13 [PATCH] dmaengine: sun4i: Set the maximum segment size Samuel Holland
@ 2022-07-01 16:28 ` Vinod Koul
  2022-07-01 16:46   ` Samuel Holland
  2022-07-05 13:04 ` Vinod Koul
  1 sibling, 1 reply; 4+ messages in thread
From: Vinod Koul @ 2022-07-01 16:28 UTC (permalink / raw)
  To: Samuel Holland
  Cc: Chen-Yu Tsai, Jernej Skrabec, Maxime Ripard, dmaengine,
	linux-arm-kernel, linux-kernel, linux-sunxi

On 20-06-22, 22:13, Samuel Holland wrote:
> The sun4i DMA engine supports transfer sizes up to 128k for normal DMA
> and 16M for dedicated DMA, as documented in the A10 and A20 manuals.
> 
> Since this is larger than the default segment size limit (64k), exposing
> the real limit reduces the number of transfers needed for a transaction.
> However, because the device can only report one segment size limit, we
> have to expose the smaller limit from normal DMA.
> 
> One complication is that the driver combines pairs of periodic transfers
> to reduce programming overhead. This only works when the period size is
> at most half of the maximum transfer size. With the default 64k segment
> size limit, this was always the case, but for normal DMA it is no longer
> guaranteed. Skip the optimization if the period is too long; even
> without it, the overhead is less than before.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> I don't have any A10 or A20 boards I can test this on.
> 
>  drivers/dma/sun4i-dma.c | 32 +++++++++++++++++++++++++++-----
>  1 file changed, 27 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
> index 93f1645ae928..f291b1b4db32 100644
> --- a/drivers/dma/sun4i-dma.c
> +++ b/drivers/dma/sun4i-dma.c
> @@ -7,6 +7,7 @@
>  #include <linux/bitmap.h>
>  #include <linux/bitops.h>
>  #include <linux/clk.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/dmaengine.h>
>  #include <linux/dmapool.h>
>  #include <linux/interrupt.h>
> @@ -122,6 +123,15 @@
>  	 SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) |				\
>  	 SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
>  
> +/*
> + * Normal DMA supports individual transfers (segments) up to 128k.
> + * Dedicated DMA supports transfers up to 16M. We can only report
> + * one size limit, so we have to use the smaller value.
> + */
> +#define SUN4I_NDMA_MAX_SEG_SIZE		SZ_128K
> +#define SUN4I_DDMA_MAX_SEG_SIZE		SZ_16M

This new define is not used, so why add?

> +#define SUN4I_DMA_MAX_SEG_SIZE		SUN4I_NDMA_MAX_SEG_SIZE
> +
>  struct sun4i_dma_pchan {
>  	/* Register base of channel */
>  	void __iomem			*base;
> @@ -155,7 +165,8 @@ struct sun4i_dma_contract {
>  	struct virt_dma_desc		vd;
>  	struct list_head		demands;
>  	struct list_head		completed_demands;
> -	int				is_cyclic;
> +	bool				is_cyclic : 1;
> +	bool				use_half_int : 1;
>  };
>  
>  struct sun4i_dma_dev {
> @@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
>  	if (promise) {
>  		vchan->contract = contract;
>  		vchan->pchan = pchan;
> -		set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1);
> +		set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);

not interrupt when cyclic?

>  		configure_pchan(pchan, promise);
>  	}
>  
> @@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
>  	 *
>  	 * Which requires half the engine programming for the same
>  	 * functionality.
> +	 *
> +	 * This only works if two periods fit in a single promise. That will
> +	 * always be the case for dedicated DMA, where the hardware has a much
> +	 * larger maximum transfer size than advertised to clients.
>  	 */
> -	nr_periods = DIV_ROUND_UP(len / period_len, 2);
> +	if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
> +		period_len *= 2;
> +		contract->use_half_int = 1;
> +	}
> +
> +	nr_periods = DIV_ROUND_UP(len, period_len);
>  	for (i = 0; i < nr_periods; i++) {
>  		/* Calculate the offset in the buffer and the length needed */
> -		offset = i * period_len * 2;
> -		plength = min((len - offset), (period_len * 2));
> +		offset = i * period_len;
> +		plength = min((len - offset), period_len);
>  		if (dir == DMA_MEM_TO_DEV)
>  			src = buf + offset;
>  		else
> @@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev)
>  	platform_set_drvdata(pdev, priv);
>  	spin_lock_init(&priv->lock);
>  
> +	dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
> +
>  	dma_cap_zero(priv->slave.cap_mask);
>  	dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
>  	dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
> -- 
> 2.35.1

-- 
~Vinod

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] dmaengine: sun4i: Set the maximum segment size
  2022-07-01 16:28 ` Vinod Koul
@ 2022-07-01 16:46   ` Samuel Holland
  0 siblings, 0 replies; 4+ messages in thread
From: Samuel Holland @ 2022-07-01 16:46 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Chen-Yu Tsai, Jernej Skrabec, Maxime Ripard, dmaengine,
	linux-arm-kernel, linux-kernel, linux-sunxi

On 7/1/22 11:28 AM, Vinod Koul wrote:
> On 20-06-22, 22:13, Samuel Holland wrote:
>> The sun4i DMA engine supports transfer sizes up to 128k for normal DMA
>> and 16M for dedicated DMA, as documented in the A10 and A20 manuals.
>>
>> Since this is larger than the default segment size limit (64k), exposing
>> the real limit reduces the number of transfers needed for a transaction.
>> However, because the device can only report one segment size limit, we
>> have to expose the smaller limit from normal DMA.
>>
>> One complication is that the driver combines pairs of periodic transfers
>> to reduce programming overhead. This only works when the period size is
>> at most half of the maximum transfer size. With the default 64k segment
>> size limit, this was always the case, but for normal DMA it is no longer
>> guaranteed. Skip the optimization if the period is too long; even
>> without it, the overhead is less than before.
>>
>> Signed-off-by: Samuel Holland <samuel@sholland.org>
>> ---
>> I don't have any A10 or A20 boards I can test this on.
>>
>>  drivers/dma/sun4i-dma.c | 32 +++++++++++++++++++++++++++-----
>>  1 file changed, 27 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
>> index 93f1645ae928..f291b1b4db32 100644
>> --- a/drivers/dma/sun4i-dma.c
>> +++ b/drivers/dma/sun4i-dma.c
>> @@ -7,6 +7,7 @@
>>  #include <linux/bitmap.h>
>>  #include <linux/bitops.h>
>>  #include <linux/clk.h>
>> +#include <linux/dma-mapping.h>
>>  #include <linux/dmaengine.h>
>>  #include <linux/dmapool.h>
>>  #include <linux/interrupt.h>
>> @@ -122,6 +123,15 @@
>>  	 SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) |				\
>>  	 SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
>>  
>> +/*
>> + * Normal DMA supports individual transfers (segments) up to 128k.
>> + * Dedicated DMA supports transfers up to 16M. We can only report
>> + * one size limit, so we have to use the smaller value.
>> + */
>> +#define SUN4I_NDMA_MAX_SEG_SIZE		SZ_128K
>> +#define SUN4I_DDMA_MAX_SEG_SIZE		SZ_16M
> 
> This new define is not used, so why add?

This documents the "much larger" claim below. I could have written:

max_period = vchan->is_dedicated ?
	SUN4I_DDMA_MAX_SEG_SIZE : SUN4I_NDMA_MAX_SEG_SIZE;
if (period_len <= max_period / 2) {
	...

But the check against SUN4I_DDMA_MAX_SEG_SIZE would be a waste of cycles,
because we already know that the period length is at most 128K.

>> +#define SUN4I_DMA_MAX_SEG_SIZE		SUN4I_NDMA_MAX_SEG_SIZE
>> +
>>  struct sun4i_dma_pchan {
>>  	/* Register base of channel */
>>  	void __iomem			*base;
>> @@ -155,7 +165,8 @@ struct sun4i_dma_contract {
>>  	struct virt_dma_desc		vd;
>>  	struct list_head		demands;
>>  	struct list_head		completed_demands;
>> -	int				is_cyclic;
>> +	bool				is_cyclic : 1;
>> +	bool				use_half_int : 1;
>>  };
>>  
>>  struct sun4i_dma_dev {
>> @@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
>>  	if (promise) {
>>  		vchan->contract = contract;
>>  		vchan->pchan = pchan;
>> -		set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1);
>> +		set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);
> 
> not interrupt when cyclic?

The hardware has two interrupts: "transfer done" and "half done". The "transfer
done" interrupt is always enabled (by the last parameter). But the "half done"
interrupt is only enabled when the transfer size has been doubled by the code in
the diff below.

Regards,
Samuel

>>  		configure_pchan(pchan, promise);
>>  	}
>>  
>> @@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
>>  	 *
>>  	 * Which requires half the engine programming for the same
>>  	 * functionality.
>> +	 *
>> +	 * This only works if two periods fit in a single promise. That will
>> +	 * always be the case for dedicated DMA, where the hardware has a much
>> +	 * larger maximum transfer size than advertised to clients.
>>  	 */
>> -	nr_periods = DIV_ROUND_UP(len / period_len, 2);
>> +	if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
>> +		period_len *= 2;
>> +		contract->use_half_int = 1;
>> +	}
>> +
>> +	nr_periods = DIV_ROUND_UP(len, period_len);
>>  	for (i = 0; i < nr_periods; i++) {
>>  		/* Calculate the offset in the buffer and the length needed */
>> -		offset = i * period_len * 2;
>> -		plength = min((len - offset), (period_len * 2));
>> +		offset = i * period_len;
>> +		plength = min((len - offset), period_len);
>>  		if (dir == DMA_MEM_TO_DEV)
>>  			src = buf + offset;
>>  		else
>> @@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev)
>>  	platform_set_drvdata(pdev, priv);
>>  	spin_lock_init(&priv->lock);
>>  
>> +	dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
>> +
>>  	dma_cap_zero(priv->slave.cap_mask);
>>  	dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
>>  	dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
>> -- 
>> 2.35.1
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] dmaengine: sun4i: Set the maximum segment size
  2022-06-21  3:13 [PATCH] dmaengine: sun4i: Set the maximum segment size Samuel Holland
  2022-07-01 16:28 ` Vinod Koul
@ 2022-07-05 13:04 ` Vinod Koul
  1 sibling, 0 replies; 4+ messages in thread
From: Vinod Koul @ 2022-07-05 13:04 UTC (permalink / raw)
  To: Samuel Holland
  Cc: Chen-Yu Tsai, Jernej Skrabec, Maxime Ripard, dmaengine,
	linux-arm-kernel, linux-kernel, linux-sunxi

On 20-06-22, 22:13, Samuel Holland wrote:
> The sun4i DMA engine supports transfer sizes up to 128k for normal DMA
> and 16M for dedicated DMA, as documented in the A10 and A20 manuals.
> 
> Since this is larger than the default segment size limit (64k), exposing
> the real limit reduces the number of transfers needed for a transaction.
> However, because the device can only report one segment size limit, we
> have to expose the smaller limit from normal DMA.
> 
> One complication is that the driver combines pairs of periodic transfers
> to reduce programming overhead. This only works when the period size is
> at most half of the maximum transfer size. With the default 64k segment
> size limit, this was always the case, but for normal DMA it is no longer
> guaranteed. Skip the optimization if the period is too long; even
> without it, the overhead is less than before.

Applied, thanks

-- 
~Vinod

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-07-05 13:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-21  3:13 [PATCH] dmaengine: sun4i: Set the maximum segment size Samuel Holland
2022-07-01 16:28 ` Vinod Koul
2022-07-01 16:46   ` Samuel Holland
2022-07-05 13:04 ` Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).