dmaengine: at_xdmac: several improvements
Subsystems (2)
v2 cover dmaengine: at_xdmac: several improvements
Hi,
This series is based on vkoul/slave-dma/topic/slave_caps_device_control_fix branch.
Changes from v1:
- put save_cc field addition in a single patch
- update commit log of patch 3/4
- remove AT_XDMAC_CUR_CFG since it is no more used
Cyrille Pitchen (1):
dmaengine: at_xdmac: wait for in-progress transaction to complete
after pausing a channel
Ludovic Desroches (3):
dmaengine: at_xdmac: introduce save_cc field
dmaengine: at_xdmac: simplify channel configuration stuff
dmaengine: at_xdmac: allow muliple dwidths when doing slave transfers
drivers/dma/at_xdmac.c | 60 ++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 29 deletions(-)
Signature · 1 line
2.0.3
PATCH v2 1/4 dmaengine: at_xdmac: wait for in-progress transaction to complete after pausing a channel
From: Cyrille Pitchen <[email protected]>
--- drivers/dma/at_xdmac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/dma/at_xdmac.c +6 -1
@@ -1136,9 +1136,14 @@ static int at_xdmac_device_pause(struct dma_chan *chan)dev_dbg (chan2dev (chan),"%s\n" , __func__); +if (test_and_set_bit (AT_XDMAC_CHAN_IS_PAUSED , &atchan->status)) +return 0 ; +spin_lock_bh (&atchan->lock);at_xdmac_write (atxdmac,AT_XDMAC_GRWS , atchan->mask); -set_bit (AT_XDMAC_CHAN_IS_PAUSED , &atchan->status); +while (at_xdmac_chan_read (atchan,AT_XDMAC_CC ) + & (AT_XDMAC_CC_WRIP |AT_XDMAC_CC_RDIP )) +cpu_relax ();spin_unlock_bh (&atchan->lock);return 0 ;
Signature · 1 line
2.0.3
PATCH v2 2/4 dmaengine: at_xdmac: introduce save_cc field
In reply to Ludovic DesrochesWhen suspending the device, read the channel configuration directly from the register instead of relying on a software snapshot, it will be safer.
--- drivers/dma/at_xdmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/dma/at_xdmac.c +3 -3
@@ -200,6 +200,7 @@ struct at_xdmac_chan {u8 memif;/* Memory Interface */ u32 per_src_addr;u32 per_dst_addr; +u32 save_cc;u32 save_cim;u32 save_cnda;u32 save_cndc;
@@ -1276,6 +1277,7 @@ static int atmel_xdmac_suspend(struct device *dev)list_for_each_entry_safe (chan, _chan, &atxdmac->dma.channels, device_node) {struct at_xdmac_chan *atchan =to_at_xdmac_chan (chan); + atchan->save_cc =at_xdmac_chan_read (atchan,AT_XDMAC_CC );if (at_xdmac_chan_is_cyclic (atchan)) {if (!at_xdmac_chan_is_paused (atchan))at_xdmac_device_pause (chan);
@@ -1298,7 +1300,6 @@ static int atmel_xdmac_resume(struct device *dev)struct at_xdmac_chan *atchan;struct dma_chan *chan, *_chan;int i; -u32 cfg;clk_prepare_enable (atxdmac->clk);
@@ -1313,8 +1314,7 @@ static int atmel_xdmac_resume(struct device *dev)at_xdmac_write (atxdmac,AT_XDMAC_GE , atxdmac->save_gs);list_for_each_entry_safe (chan, _chan, &atxdmac->dma.channels, device_node) { atchan =to_at_xdmac_chan (chan); - cfg = atchan->cfg[AT_XDMAC_CUR_CFG ]; -at_xdmac_chan_write (atchan,AT_XDMAC_CC , cfg); +at_xdmac_chan_write (atchan,AT_XDMAC_CC , atchan->save_cc);if (at_xdmac_chan_is_cyclic (atchan)) {at_xdmac_chan_write (atchan,AT_XDMAC_CNDA , atchan->save_cnda);at_xdmac_chan_write (atchan,AT_XDMAC_CNDC , atchan->save_cndc);
Signature · 1 line
2.0.3
PATCH v2 3/4 dmaengine: at_xdmac: simplify channel configuration stuff
In reply to Ludovic DesrochesThis patch simplifies the channel configuration register management. Relying on a "software snapshot" of the configuration is not safe and too complex.
Multiple dwidths will be introduced for slave transfers. In this case, it becomes quite difficult to have an accurate snapshot of the channel configuration register in the way it is done. Using the channel configuration available in the lli descriptor simplifies this stuff.
--- drivers/dma/at_xdmac.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-)
drivers/dma/at_xdmac.c +14 -22
@@ -191,10 +191,9 @@ struct at_xdmac_chan {struct dma_chan chan;void __iomem *ch_regs;u32 mask;/* Channel Mask */ -u32 cfg[3 ];/* Channel Configuration Register */ -#define AT_XDMAC_CUR_CFG 0/* Current channel conf */ -#define AT_XDMAC_DEV_TO_MEM_CFG 1/* Predifined dev to mem channel conf */ -#define AT_XDMAC_MEM_TO_DEV_CFG 2/* Predifined mem to dev channel conf */ +u32 cfg[2 ];/* Channel Configuration Register */ +#define AT_XDMAC_DEV_TO_MEM_CFG 0/* Predifined dev to mem channel conf */ +#define AT_XDMAC_MEM_TO_DEV_CFG 1/* Predifined mem to dev channel conf */ u8 perid;/* Peripheral ID */ u8 perif;/* Peripheral Interface */ u8 memif;/* Memory Interface */
@@ -358,14 +357,7 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,*/ if (is_slave_direction (first->direction)) { reg =AT_XDMAC_CNDC_NDVIEW_NDV1 ; -if (first->direction ==DMA_MEM_TO_DEV ) - atchan->cfg[AT_XDMAC_CUR_CFG ] = - atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG ]; -else - atchan->cfg[AT_XDMAC_CUR_CFG ] = - atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG ]; -at_xdmac_chan_write (atchan,AT_XDMAC_CC , - atchan->cfg[AT_XDMAC_CUR_CFG ]); +at_xdmac_chan_write (atchan,AT_XDMAC_CC , first->lld.mbr_cfg); }else {/* * No need to write AT_XDMAC_CC reg, it will be done when the
@@ -569,7 +561,6 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,struct at_xdmac_desc *first =NULL , *prev =NULL ;struct scatterlist *sg;int i; -u32 cfg;unsigned int xfer_size =0 ;if (!sgl)
@@ -616,17 +607,17 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,if (direction ==DMA_DEV_TO_MEM ) { desc->lld.mbr_sa = atchan->per_src_addr; desc->lld.mbr_da = mem; - cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG ]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG ]; }else { desc->lld.mbr_sa = mem; desc->lld.mbr_da = atchan->per_dst_addr; - cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG ]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG ]; } - desc->lld.mbr_ubc =AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ - |AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ - |AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ - | (i == sg_len -1 ?0 :AT_XDMAC_MBR_UBC_NDE )/* descriptor fetch */ - | len / (1 <<at_xdmac_get_dwidth (cfg));/* microblock length */ + desc->lld.mbr_ubc =AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ + |AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ + |AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ + | (i == sg_len -1 ?0 :AT_XDMAC_MBR_UBC_NDE )/* descriptor fetch */ + | len / (1 <<at_xdmac_get_dwidth (desc->lld.mbr_cfg));/* microblock length */ dev_dbg (chan2dev (chan),"%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n" , __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc);
@@ -890,7 +881,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,enum dma_status ret;int residue;u32 cur_nda, mask, value; -u8 dwidth =at_xdmac_get_dwidth (atchan->cfg[AT_XDMAC_CUR_CFG ]); +u8 dwidth =0 ; ret =dma_cookie_status (chan, cookie, txstate);if (ret ==DMA_COMPLETE )
@@ -920,7 +911,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,*/ mask =AT_XDMAC_CC_TYPE |AT_XDMAC_CC_DSYNC ; value =AT_XDMAC_CC_TYPE_PER_TRAN |AT_XDMAC_CC_DSYNC_PER2MEM ; -if ((atchan->cfg[AT_XDMAC_CUR_CFG ] & mask) == value) { +if ((desc->lld.mbr_cfg & mask) == value) {at_xdmac_write (atxdmac,AT_XDMAC_GSWF , atchan->mask);while (!(at_xdmac_chan_read (atchan,AT_XDMAC_CIS ) &AT_XDMAC_CIS_FIS ))cpu_relax ();
@@ -934,6 +925,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,*/ descs_list = &desc->descs_list;list_for_each_entry_safe (desc, _desc, descs_list, desc_node) { + dwidth =at_xdmac_get_dwidth (desc->lld.mbr_cfg); residue -= (desc->lld.mbr_ubc &0xffffff ) << dwidth;if ((desc->lld.mbr_nda &0xfffffffc ) == cur_nda)break ;
Signature · 1 line
2.0.3
PATCH v2 4/4 dmaengine: at_xdmac: allow muliple dwidths when doing slave transfers
In reply to Ludovic DesrochesWhen using FIFO, we need to support differents data width in a single transfer. For example, serial device which usually uses 1-byte data width will use 4-bytes data width when using the FIFO. If the transfer size is not aligned on 4-bytes then the end of the transfer will be performed with 1-byte data-width. For that reason, at_xdmac_prep_slave_sg() now builds linked list descriptors using view 2 instead of view 1 so each of them can update the DWIDTH field into the Channel Configuration Register.
--- drivers/dma/at_xdmac.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
drivers/dma/at_xdmac.c +10 -5
@@ -25,6 +25,7 @@#include <linux/dmapool.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of_dma.h>
@@ -351,11 +352,11 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,at_xdmac_chan_write (atchan,AT_XDMAC_CNDA , reg);/* -* When doing memory to memory transfer we need to use the next +* When doing non cyclic transfer we need to use the next * descriptor view 2 since some fields of the configuration register * depend on transfer size and src/dest addresses. */ -if (is_slave_direction (first->direction)) { +if (at_xdmac_chan_is_cyclic (atchan)) { reg =AT_XDMAC_CNDC_NDVIEW_NDV1 ;at_xdmac_chan_write (atchan,AT_XDMAC_CC , first->lld.mbr_cfg); }else {
@@ -582,7 +583,7 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,/* Prepare descriptors. */ for_each_sg (sgl, sg, sg_len, i) {struct at_xdmac_desc *desc =NULL ; -u32 len, mem; +u32 len, mem, dwidth, fixed_dwidth; len =sg_dma_len (sg); mem =sg_dma_address (sg);
@@ -613,11 +614,15 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->lld.mbr_da = atchan->per_dst_addr; desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG ]; } - desc->lld.mbr_ubc =AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ + dwidth =at_xdmac_get_dwidth (desc->lld.mbr_cfg); + fixed_dwidth =IS_ALIGNED (len,1 << dwidth) + ?at_xdmac_get_dwidth (desc->lld.mbr_cfg) + :AT_XDMAC_CC_DWIDTH_BYTE ; + desc->lld.mbr_ubc =AT_XDMAC_MBR_UBC_NDV2 /* next descriptor view */ |AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ |AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ | (i == sg_len -1 ?0 :AT_XDMAC_MBR_UBC_NDE )/* descriptor fetch */ - | len / (1 <<at_xdmac_get_dwidth (desc->lld.mbr_cfg));/* microblock length */ + | (len >> fixed_dwidth);/* microblock length */ dev_dbg (chan2dev (chan),"%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n" , __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc);
Signature · 1 line
2.0.3
v2 cover dmaengine: at_xdmac: several improvements
In reply to Ludovic DesrochesHi Vinod,
Any issue with this patch series?
Regards
Ludovic
Quoted 25 lines · Ludovic Desroches
Hi,
This series is based on vkoul/slave-dma/topic/slave_caps_device_control_fix branch.
Changes from v1:
- putsave_ccfield addition in a single patch
- update commit log of patch 3/4
- removeAT_XDMAC_CUR_CFGsince it is no more usedCyrille Pitchen (1):
dmaengine:at_xdmac: wait for in-progress transaction to complete
after pausing a channelLudovic Desroches (3):
dmaengine:at_xdmac: introducesave_ccfield
dmaengine:at_xdmac: simplify channel configuration stuff
dmaengine:at_xdmac: allow muliple dwidths when doing slave transfersdrivers/dma/at_xdmac.c | 60 ++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 29 deletions(-)Signature · 1 line
2.0.3