diff mbox series

[RFC,2/3] dmaengine: add peripheral configuration

Message ID 20200824084712.2526079-3-vkoul@kernel.org
State New
Headers show
Series [1/3] dt-bindings: dmaengine: Document qcom,gpi dma binding | expand

Commit Message

Vinod Koul Aug. 24, 2020, 8:47 a.m. UTC
Some complex dmaengine controllers have capability to program the
peripheral device, so pass on the peripheral configuration as part of
dma_slave_config

Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
 include/linux/dmaengine.h | 75 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

Comments

Peter Ujfalusi Aug. 25, 2020, 6:52 a.m. UTC | #1
Hi Vinod,

On 24/08/2020 11.47, Vinod Koul wrote:
> Some complex dmaengine controllers have capability to program the
> peripheral device, so pass on the peripheral configuration as part of
> dma_slave_config
> 
> Signed-off-by: Vinod Koul <vkoul@kernel.org>
> ---
>  include/linux/dmaengine.h | 75 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 75 insertions(+)
> 
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 6fbd5c99e30c..264643ca9209 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -380,6 +380,73 @@ enum dma_slave_buswidth {
>  	DMA_SLAVE_BUSWIDTH_64_BYTES = 64,
>  };
>  
> +/**
> + * enum spi_transfer_cmd - spi transfer commands
> + */
> +enum spi_transfer_cmd {
> +	SPI_TX = 1,
> +	SPI_RX,
> +	SPI_DUPLEX,
> +};
> +
> +/**
> + * struct dmaengine_spi_config - spi config for peripheral
> + *
> + * @loopback_en: spi loopback enable when set
> + * @clock_pol: clock polarity
> + * @data_pol: data polarity
> + * @pack_en: process tx/rx buffers as packed
> + * @word_len: spi word length
> + * @clk_div: source clock divider
> + * @clk_src: serial clock
> + * @cmd: spi cmd
> + * @cs: chip select toggle
> + * @rx_len: receive length for spi buffer
> + */
> +struct dmaengine_spi_config {
> +	u8 loopback_en;
> +	u8 clock_pol;
> +	u8 data_pol;
> +	u8 pack_en;
> +	u8 word_len;
> +	u32 clk_div;
> +	u32 clk_src;
> +	u8 fragmentation;
> +	enum spi_transfer_cmd cmd;
> +	u8 cs;
> +	u32 rx_len;
> +};
> +
> +/**
> + * struct dmaengine_i2c_config - i2c config for peripheral
> + *
> + * @pack_enable: process tx/rx buffers as packed
> + * @cycle_count: clock cycles to be sent
> + * @high_count: high period of clock
> + * @low_count: low period of clock
> + * @clk_div: source clock divider
> + * @addr: i2c bus address
> + * @strech: strech the clock at eot
> + * @op: i2c cmd
> + */
> +struct dmaengine_i2c_config {
> +	u8 pack_enable;
> +	u8 cycle_count;
> +	u8 high_count;
> +	u8 low_count;
> +	u16 clk_div;
> +	u8 addr;
> +	u8 strech;
> +	u8 op;
> +};
> +
> +enum dmaengine_peripheral {
> +	DMAENGINE_PERIPHERAL_SPI,
> +	DMAENGINE_PERIPHERAL_I2C,
> +	DMAENGINE_PERIPHERAL_UART,
> +	DMAENGINE_PERIPHERAL_LAST = DMAENGINE_PERIPHERAL_UART,
> +};
> +
>  /**
>   * struct dma_slave_config - dma slave channel runtime config
>   * @direction: whether the data shall go in or out on this slave
> @@ -418,6 +485,10 @@ enum dma_slave_buswidth {
>   * @slave_id: Slave requester id. Only valid for slave channels. The dma
>   * slave peripheral will have unique id as dma requester which need to be
>   * pass as slave config.
> + * @peripheral: type of peripheral to DMA to/from
> + * @set_config: set peripheral config
> + * @spi: peripheral config for spi
> + * @:i2c peripheral config for i2c
>   *
>   * This struct is passed in as configuration data to a DMA engine
>   * in order to set up a certain channel for DMA transport at runtime.
> @@ -443,6 +514,10 @@ struct dma_slave_config {
>  	u32 dst_port_window_size;
>  	bool device_fc;
>  	unsigned int slave_id;
> +	enum dmaengine_peripheral peripheral;
> +	u8 set_config;
> +	struct dmaengine_spi_config spi;
> +	struct dmaengine_i2c_config i2c;

Would it be possible to reuse one of the existing feature already
supported by DMAengine?
We have DMA_PREP_CMD to give a command instead of a real transfer:
dmaengine_prep_slave_single(tx_chan, config_data, config_len,
			    DMA_MEM_TO_DEV, DMA_PREP_CMD);
dmaengine_prep_slave_single(tx_chan, tx_buff, tx_len, DMA_MEM_TO_DEV,
			    DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
dma_async_issue_pending(tx_chan);

or the metadata support:
tx = dmaengine_prep_slave_single(tx_chan, tx_buff, tx_len,
				 DMA_MEM_TO_DEV,
				 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
dmaengine_desc_attach_metadata(tx, config_data, config_len);
dma_async_issue_pending(tx_chan);

By reading the driver itself, it is not clear if you always need to send
the config for TX, or only when the config is changing and what happens
if the first transfer (for SPI, since that is the only implemented one)
is RX, when you don't send config at all...

I'm concerned about the size increase of dma_slave_config (it grows by
>30 bytes) and for DMAs with hundreds of channels (UDMA) it will add up
to a sizeable amount.

>  };
>  
>  /**
> 

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Vinod Koul Aug. 25, 2020, 7:10 a.m. UTC | #2
Hello Peter,

On 25-08-20, 09:52, Peter Ujfalusi wrote:
> Hi Vinod,
> 
> On 24/08/2020 11.47, Vinod Koul wrote:
> > Some complex dmaengine controllers have capability to program the
> > peripheral device, so pass on the peripheral configuration as part of
> > dma_slave_config
> > 
> > Signed-off-by: Vinod Koul <vkoul@kernel.org>
> > ---
> >  include/linux/dmaengine.h | 75 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 75 insertions(+)
> > 
> > diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> > index 6fbd5c99e30c..264643ca9209 100644
> > --- a/include/linux/dmaengine.h
> > +++ b/include/linux/dmaengine.h
> > @@ -380,6 +380,73 @@ enum dma_slave_buswidth {
> >  	DMA_SLAVE_BUSWIDTH_64_BYTES = 64,
> >  };
> >  
> > +/**
> > + * enum spi_transfer_cmd - spi transfer commands
> > + */
> > +enum spi_transfer_cmd {
> > +	SPI_TX = 1,
> > +	SPI_RX,
> > +	SPI_DUPLEX,
> > +};
> > +
> > +/**
> > + * struct dmaengine_spi_config - spi config for peripheral
> > + *
> > + * @loopback_en: spi loopback enable when set
> > + * @clock_pol: clock polarity
> > + * @data_pol: data polarity
> > + * @pack_en: process tx/rx buffers as packed
> > + * @word_len: spi word length
> > + * @clk_div: source clock divider
> > + * @clk_src: serial clock
> > + * @cmd: spi cmd
> > + * @cs: chip select toggle
> > + * @rx_len: receive length for spi buffer
> > + */
> > +struct dmaengine_spi_config {
> > +	u8 loopback_en;
> > +	u8 clock_pol;
> > +	u8 data_pol;
> > +	u8 pack_en;
> > +	u8 word_len;
> > +	u32 clk_div;
> > +	u32 clk_src;
> > +	u8 fragmentation;
> > +	enum spi_transfer_cmd cmd;
> > +	u8 cs;
> > +	u32 rx_len;
> > +};
> > +
> > +/**
> > + * struct dmaengine_i2c_config - i2c config for peripheral
> > + *
> > + * @pack_enable: process tx/rx buffers as packed
> > + * @cycle_count: clock cycles to be sent
> > + * @high_count: high period of clock
> > + * @low_count: low period of clock
> > + * @clk_div: source clock divider
> > + * @addr: i2c bus address
> > + * @strech: strech the clock at eot
> > + * @op: i2c cmd
> > + */
> > +struct dmaengine_i2c_config {
> > +	u8 pack_enable;
> > +	u8 cycle_count;
> > +	u8 high_count;
> > +	u8 low_count;
> > +	u16 clk_div;
> > +	u8 addr;
> > +	u8 strech;
> > +	u8 op;
> > +};
> > +
> > +enum dmaengine_peripheral {
> > +	DMAENGINE_PERIPHERAL_SPI,
> > +	DMAENGINE_PERIPHERAL_I2C,
> > +	DMAENGINE_PERIPHERAL_UART,
> > +	DMAENGINE_PERIPHERAL_LAST = DMAENGINE_PERIPHERAL_UART,
> > +};
> > +
> >  /**
> >   * struct dma_slave_config - dma slave channel runtime config
> >   * @direction: whether the data shall go in or out on this slave
> > @@ -418,6 +485,10 @@ enum dma_slave_buswidth {
> >   * @slave_id: Slave requester id. Only valid for slave channels. The dma
> >   * slave peripheral will have unique id as dma requester which need to be
> >   * pass as slave config.
> > + * @peripheral: type of peripheral to DMA to/from
> > + * @set_config: set peripheral config
> > + * @spi: peripheral config for spi
> > + * @:i2c peripheral config for i2c
> >   *
> >   * This struct is passed in as configuration data to a DMA engine
> >   * in order to set up a certain channel for DMA transport at runtime.
> > @@ -443,6 +514,10 @@ struct dma_slave_config {
> >  	u32 dst_port_window_size;
> >  	bool device_fc;
> >  	unsigned int slave_id;
> > +	enum dmaengine_peripheral peripheral;
> > +	u8 set_config;
> > +	struct dmaengine_spi_config spi;
> > +	struct dmaengine_i2c_config i2c;
> 
> Would it be possible to reuse one of the existing feature already
> supported by DMAengine?
> We have DMA_PREP_CMD to give a command instead of a real transfer:
> dmaengine_prep_slave_single(tx_chan, config_data, config_len,
> 			    DMA_MEM_TO_DEV, DMA_PREP_CMD);
> dmaengine_prep_slave_single(tx_chan, tx_buff, tx_len, DMA_MEM_TO_DEV,
> 			    DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> dma_async_issue_pending(tx_chan);
> 
> or the metadata support:
> tx = dmaengine_prep_slave_single(tx_chan, tx_buff, tx_len,
> 				 DMA_MEM_TO_DEV,
> 				 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> dmaengine_desc_attach_metadata(tx, config_data, config_len);
> dma_async_issue_pending(tx_chan);
> 
> By reading the driver itself, it is not clear if you always need to send
> the config for TX, or only when the config is changing and what happens
> if the first transfer (for SPI, since that is the only implemented one)
> is RX, when you don't send config at all...

So this config is sent to driver everytime before the prep call (can be
optimized to once if we have similar transfers in queue).

This config is used to create the configuration passed to dmaengine
which is used to actually program both dmaengine as well as peripheral
registers (i2c/spi/etc), so we need a way to pass the spi/i2c config.

I think prep cmd can be used to send this data, I do not see any issues
with that, it would work if we want to go that route.

I did have a prototype with metadata but didnt work very well, the
problem is it assumes metadata for tx/rx but here i send the data
everytime from client data.

> I'm concerned about the size increase of dma_slave_config (it grows by
> >30 bytes) and for DMAs with hundreds of channels (UDMA) it will add up
> to a sizeable amount.

I agree that is indeed a valid concern, that is the reason I tagged this
as a RFC patch ;-)

I see the prep_cmd is a better approach for this, anyone else has better
suggestions?

Thanks for looking in.
Peter Ujfalusi Aug. 26, 2020, 7:07 a.m. UTC | #3
Hi Vinod,

On 25/08/2020 14.02, Vinod Koul wrote:
>> The only thing which might be an issue is that with the DMA_PREP_CMD the
>> config_data is dma_addr_t (via dmaengine_prep_slave_single).
> 
> Yes I came to same conclusion
> 
>>> I did have a prototype with metadata but didnt work very well, the
>>> problem is it assumes metadata for tx/rx but here i send the data
>>> everytime from client data.
>>
>> Yes, the intended use case for metadata (per descriptor!) is for
>> channels where each transfer might have different metadata needed for
>> the given transfer (tx/rx).
>>
>> In your case you have semi static peripheral configuration data, which
>> is not really changing between transfers.
>>
>> A compromise would be to add:
>> void *peripheral_config;
>> to the dma_slave_config, move the set_config inside of the device
>> specific struct you are passing from a client to the core?
> 
> That sounds more saner to me and uses existing interfaces cleanly. I
> think I like this option ;-)

The other option would be to use the descriptor metadata support and
that might be even cleaner.

In gpi_create_tre() via gpi_prep_slave_sg() you would set up the
desc->tre[1] and desc->tre[2] for TX
desc->tre[2] for RX
in the desc, you add a new variable, let's say first_tre and
set it to 1 for TX, 2 for RX.

If you need to send a config, you attach it via either way metadata
support allows you (get the pointer to desc->tre[0] or give a config
struct to the DMA driver.

In the metadata handler, you check if the transfer is TX, if it is, then
you update desc->tre[0] (or give the pointer to the client) and update
the first_tre to 0.

In issue_pending, or a small helper which can be used to start the
transfer you would do the queuing instead of prepare time:
for (i = gpi_desc->first_tre; i < MAX_TRE; i++)
	gpi_queue_xfer(gpii, gpii_chan,  &gpi_desc->tre[i], &wp);

With this change it should work neatly without any change to
dma_slave_config.

> 
>>>> I'm concerned about the size increase of dma_slave_config (it grows by
>>>>> 30 bytes) and for DMAs with hundreds of channels (UDMA) it will add up
>>>> to a sizeable amount.
>>>
>>> I agree that is indeed a valid concern, that is the reason I tagged this
>>> as a RFC patch ;-)
>>>
>>> I see the prep_cmd is a better approach for this, anyone else has better
>>> suggestions?
>>>
>>> Thanks for looking in.
>>>
>>
>> - Péter
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
> 

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
diff mbox series

Patch

diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 6fbd5c99e30c..264643ca9209 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -380,6 +380,73 @@  enum dma_slave_buswidth {
 	DMA_SLAVE_BUSWIDTH_64_BYTES = 64,
 };
 
+/**
+ * enum spi_transfer_cmd - spi transfer commands
+ */
+enum spi_transfer_cmd {
+	SPI_TX = 1,
+	SPI_RX,
+	SPI_DUPLEX,
+};
+
+/**
+ * struct dmaengine_spi_config - spi config for peripheral
+ *
+ * @loopback_en: spi loopback enable when set
+ * @clock_pol: clock polarity
+ * @data_pol: data polarity
+ * @pack_en: process tx/rx buffers as packed
+ * @word_len: spi word length
+ * @clk_div: source clock divider
+ * @clk_src: serial clock
+ * @cmd: spi cmd
+ * @cs: chip select toggle
+ * @rx_len: receive length for spi buffer
+ */
+struct dmaengine_spi_config {
+	u8 loopback_en;
+	u8 clock_pol;
+	u8 data_pol;
+	u8 pack_en;
+	u8 word_len;
+	u32 clk_div;
+	u32 clk_src;
+	u8 fragmentation;
+	enum spi_transfer_cmd cmd;
+	u8 cs;
+	u32 rx_len;
+};
+
+/**
+ * struct dmaengine_i2c_config - i2c config for peripheral
+ *
+ * @pack_enable: process tx/rx buffers as packed
+ * @cycle_count: clock cycles to be sent
+ * @high_count: high period of clock
+ * @low_count: low period of clock
+ * @clk_div: source clock divider
+ * @addr: i2c bus address
+ * @strech: strech the clock at eot
+ * @op: i2c cmd
+ */
+struct dmaengine_i2c_config {
+	u8 pack_enable;
+	u8 cycle_count;
+	u8 high_count;
+	u8 low_count;
+	u16 clk_div;
+	u8 addr;
+	u8 strech;
+	u8 op;
+};
+
+enum dmaengine_peripheral {
+	DMAENGINE_PERIPHERAL_SPI,
+	DMAENGINE_PERIPHERAL_I2C,
+	DMAENGINE_PERIPHERAL_UART,
+	DMAENGINE_PERIPHERAL_LAST = DMAENGINE_PERIPHERAL_UART,
+};
+
 /**
  * struct dma_slave_config - dma slave channel runtime config
  * @direction: whether the data shall go in or out on this slave
@@ -418,6 +485,10 @@  enum dma_slave_buswidth {
  * @slave_id: Slave requester id. Only valid for slave channels. The dma
  * slave peripheral will have unique id as dma requester which need to be
  * pass as slave config.
+ * @peripheral: type of peripheral to DMA to/from
+ * @set_config: set peripheral config
+ * @spi: peripheral config for spi
+ * @:i2c peripheral config for i2c
  *
  * This struct is passed in as configuration data to a DMA engine
  * in order to set up a certain channel for DMA transport at runtime.
@@ -443,6 +514,10 @@  struct dma_slave_config {
 	u32 dst_port_window_size;
 	bool device_fc;
 	unsigned int slave_id;
+	enum dmaengine_peripheral peripheral;
+	u8 set_config;
+	struct dmaengine_spi_config spi;
+	struct dmaengine_i2c_config i2c;
 };
 
 /**