Fixes / cleanups in dw_dmac (affects on few subsystems)
Subsystems (12)
Andy Shevchenko
reviewerv2 cover Fixes / cleanups in dw_dmac (affects on few subsystems)
This patch series (v1: http://www.spinics.net/lists/dmaengine/msg07930.html)
contains a number of mostly minor fixes and cleanups for the DW DMA driver. A couple of them affect the DT binding so these may need to be updated to maintain compatibility. The rest should be relatively straight-forward.
Changes since v1:
- zeroing struct dw_dma_slave before use
- fall back to old data_width property if data-width is not found
- append tags for few patches
- correct title of cover letter
- rebase on top of recent linux-next
Andy Shevchenko (11):
dmaengine: dw: rename masters to reflect actual topology
dmaengine: dw: substitute dma_read_byaddr by dma_readl_native
dmaengine: dw: revisit data_width property
dmaengine: dw: define counter variables as unsigned int
dmaengine: dw: keep entire platform data in struct dw_dma
dmaengine: dw: pass platform data via struct dw_dma_chip
dmaengine: dw: platform: use field-by-field initialization
dmaengine: dw: move dwc->paused to dwc->flags
dmaengine: dw: move dwc->initialized to dwc->flags
dmaengine: dw: move residue to a descriptor
dmaengine: dw: set cdesc to NULL when free cyclic transfers
Mans Rullgard (4):
dmaengine: dw: fix byte order of hw descriptor fields
dmaengine: dw: clear LLP_[SD]_EN bits in last descriptor of a chain
dmaengine: dw: set src and dst master select according to xfer
direction
dmaengine: dw: set LMS field in descriptors
Documentation/devicetree/bindings/dma/snps-dma.txt | 9 +- arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +- arch/arm/boot/dts/spear13xx.dtsi | 4 +- arch/avr32/mach-at32ap/at32ap700x.c | 16 +- drivers/ata/sata_dwc_460ex.c | 6 +- drivers/dma/dw/core.c | 299 ++++++++++----------- drivers/dma/dw/pci.c | 3 +- drivers/dma/dw/platform.c | 40 +-- drivers/dma/dw/regs.h | 55 ++-- drivers/spi/spi-pxa2xx-pci.c | 8 +- drivers/tty/serial/8250/8250_pci.c | 8 +- include/linux/dma/dw.h | 14 +- include/linux/platform_data/dma-dw.h | 15 +- sound/soc/intel/common/sst-firmware.c | 2 +- 14 files changed, 245 insertions(+), 236 deletions(-)
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 1/15 dmaengine: dw: fix byte order of hw descriptor fields
From: Mans Rullgard <[email protected]>
If the DMA controller uses a different byte order than the host CPU, the hardware linked list descriptor fields need to be byte-swapped.
This patch makes the driver write these fields using the same byte order it uses for mmio accesses to the DMA engine. I do not know if this is guaranteed to always be correct.
--- drivers/dma/dw/core.c | 109 ++++++++++++++++++++++++-------------------------- drivers/dma/dw/regs.h | 32 +++++++++++---- 2 files changed, 78 insertions(+), 63 deletions(-)
drivers/dma/dw/core.c +53 -56
@@ -209,12 +209,12 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc,* Software emulation of LLP mode relies on interrupts to continue * multi block transfer. */ - ctllo = desc->lli.ctllo |DWC_CTLL_INT_EN ; + ctllo =lli_read (desc, ctllo) |DWC_CTLL_INT_EN ; -channel_writel (dwc,SAR , desc->lli.sar); -channel_writel (dwc,DAR , desc->lli.dar); +channel_writel (dwc,SAR ,lli_read (desc, sar)); +channel_writel (dwc,DAR ,lli_read (desc, dar));channel_writel (dwc,CTL_LO , ctllo); -channel_writel (dwc,CTL_HI , desc->lli.ctlhi); +channel_writel (dwc,CTL_HI ,lli_read (desc, ctlhi));channel_set_bit (dw,CH_EN , dwc->mask);/* Move pointer to next descriptor */
@@ -432,7 +432,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) }/* Check first descriptors llp */ -if (desc->lli.llp == llp) { +if (lli_read (desc, llp) == llp) {/* This one is currently in progress */ dwc->residue -=dwc_get_sent (dwc);spin_unlock_irqrestore (&dwc->lock, flags);
@@ -441,7 +441,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc->residue -= desc->len;list_for_each_entry (child, &desc->tx_list, desc_node) { -if (child->lli.llp == llp) { +if (lli_read (child, llp) == llp) {/* Currently in progress */ dwc->residue -=dwc_get_sent (dwc);spin_unlock_irqrestore (&dwc->lock, flags);
@@ -743,25 +743,24 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,if (!desc) goto err_desc_get; - desc->lli.sar = src + offset; - desc->lli.dar = dest + offset; - desc->lli.ctllo = ctllo; - desc->lli.ctlhi = xfer_count; +lli_write (desc, sar, src + offset); +lli_write (desc, dar, dest + offset); +lli_write (desc, ctllo, ctllo); +lli_write (desc, ctlhi, xfer_count); desc->len = xfer_count << src_width;if (!first) { first = desc; }else { - prev->lli.llp = desc->txd.phys; -list_add_tail (&desc->desc_node, - &first->tx_list); +lli_write (prev, llp, desc->txd.phys); +list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc; }if (flags &DMA_PREP_INTERRUPT )/* Trigger interrupt after last block */ - prev->lli.ctllo |=DWC_CTLL_INT_EN ; +lli_set (prev, ctllo,DWC_CTLL_INT_EN ); prev->lli.llp =0 ; first->txd.flags = flags;
@@ -831,9 +830,9 @@ slave_sg_todev_fill_desc:if (!desc) goto err_desc_get; - desc->lli.sar = mem; - desc->lli.dar = reg; - desc->lli.ctllo = ctllo |DWC_CTLL_SRC_WIDTH (mem_width); +lli_write (desc, sar, mem); +lli_write (desc, dar, reg); +lli_write (desc, ctllo, ctllo |DWC_CTLL_SRC_WIDTH (mem_width));if ((len >> mem_width) > dwc->block_size) { dlen = dwc->block_size << mem_width; mem += dlen;
@@ -843,15 +842,14 @@ slave_sg_todev_fill_desc: len =0 ; } - desc->lli.ctlhi = dlen >> mem_width; +lli_write (desc, ctlhi, dlen >> mem_width); desc->len = dlen;if (!first) { first = desc; }else { - prev->lli.llp = desc->txd.phys; -list_add_tail (&desc->desc_node, - &first->tx_list); +lli_write (prev, llp, desc->txd.phys); +list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc; total_len += dlen;
@@ -888,9 +886,9 @@ slave_sg_fromdev_fill_desc:if (!desc) goto err_desc_get; - desc->lli.sar = reg; - desc->lli.dar = mem; - desc->lli.ctllo = ctllo |DWC_CTLL_DST_WIDTH (mem_width); +lli_write (desc, sar, reg); +lli_write (desc, dar, mem); +lli_write (desc, ctllo, ctllo |DWC_CTLL_DST_WIDTH (mem_width));if ((len >> reg_width) > dwc->block_size) { dlen = dwc->block_size << reg_width; mem += dlen;
@@ -899,15 +897,14 @@ slave_sg_fromdev_fill_desc: dlen = len; len =0 ; } - desc->lli.ctlhi = dlen >> reg_width; +lli_write (desc, ctlhi, dlen >> reg_width); desc->len = dlen;if (!first) { first = desc; }else { - prev->lli.llp = desc->txd.phys; -list_add_tail (&desc->desc_node, - &first->tx_list); +lli_write (prev, llp, desc->txd.phys); +list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc; total_len += dlen;
@@ -922,7 +919,7 @@ slave_sg_fromdev_fill_desc:if (flags &DMA_PREP_INTERRUPT )/* Trigger interrupt after last block */ - prev->lli.ctllo |=DWC_CTLL_INT_EN ; +lli_set (prev, ctllo,DWC_CTLL_INT_EN ); prev->lli.llp =0 ; first->total_len = total_len;
@@ -1393,50 +1390,50 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,switch (direction) {case DMA_MEM_TO_DEV : - desc->lli.dar = sconfig->dst_addr; - desc->lli.sar = buf_addr + (period_len * i); - desc->lli.ctllo = (DWC_DEFAULT_CTLLO (chan) - |DWC_CTLL_DST_WIDTH (reg_width) - |DWC_CTLL_SRC_WIDTH (reg_width) - |DWC_CTLL_DST_FIX - |DWC_CTLL_SRC_INC - |DWC_CTLL_INT_EN ); - - desc->lli.ctllo |= sconfig->device_fc ? -DWC_CTLL_FC (DW_DMA_FC_P_M2P ) : -DWC_CTLL_FC (DW_DMA_FC_D_M2P ); +lli_write (desc, dar, sconfig->dst_addr); +lli_write (desc, sar, buf_addr + period_len * i); +lli_write (desc, ctllo, (DWC_DEFAULT_CTLLO (chan) + |DWC_CTLL_DST_WIDTH (reg_width) + |DWC_CTLL_SRC_WIDTH (reg_width) + |DWC_CTLL_DST_FIX + |DWC_CTLL_SRC_INC + |DWC_CTLL_INT_EN )); + +lli_set (desc, ctllo, sconfig->device_fc ? +DWC_CTLL_FC (DW_DMA_FC_P_M2P ) : +DWC_CTLL_FC (DW_DMA_FC_D_M2P ));break ;case DMA_DEV_TO_MEM : - desc->lli.dar = buf_addr + (period_len * i); - desc->lli.sar = sconfig->src_addr; - desc->lli.ctllo = (DWC_DEFAULT_CTLLO (chan) - |DWC_CTLL_SRC_WIDTH (reg_width) - |DWC_CTLL_DST_WIDTH (reg_width) - |DWC_CTLL_DST_INC - |DWC_CTLL_SRC_FIX - |DWC_CTLL_INT_EN ); - - desc->lli.ctllo |= sconfig->device_fc ? -DWC_CTLL_FC (DW_DMA_FC_P_P2M ) : -DWC_CTLL_FC (DW_DMA_FC_D_P2M ); +lli_write (desc, dar, buf_addr + period_len * i); +lli_write (desc, sar, sconfig->src_addr); +lli_write (desc, ctllo, (DWC_DEFAULT_CTLLO (chan) + |DWC_CTLL_SRC_WIDTH (reg_width) + |DWC_CTLL_DST_WIDTH (reg_width) + |DWC_CTLL_DST_INC + |DWC_CTLL_SRC_FIX + |DWC_CTLL_INT_EN )); + +lli_set (desc, ctllo, sconfig->device_fc ? +DWC_CTLL_FC (DW_DMA_FC_P_P2M ) : +DWC_CTLL_FC (DW_DMA_FC_D_P2M ));break ;default :break ; } - desc->lli.ctlhi = (period_len >> reg_width); +lli_write (desc, ctlhi, period_len >> reg_width); cdesc->desc[i] = desc;if (last) - last->lli.llp = desc->txd.phys; +lli_write (last, llp, desc->txd.phys); last = desc; }/* Let's make a cyclic list */ - last->lli.llp = cdesc->desc[0 ]->txd.phys; +lli_write (last, llp, cdesc->desc[0 ]->txd.phys); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf %pad len %zu period %zu periods %d\n",
drivers/dma/dw/regs.h +25 -7
@@ -308,26 +308,44 @@ static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) return container_of(ddev, struct dw_dma, dma); } +#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO +typedef __be32 __dw32 ; +#else +typedef __le32 __dw32 ; +#endif +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ struct dw_lli {/* values that are not changed by hardware */ -u32 sar; -u32 dar; -u32 llp;/* chain to next lli */ -u32 ctllo; +__dw32 sar; +__dw32 dar; +__dw32 llp;/* chain to next lli */ +__dw32 ctllo;/* values that may get written back: */ -u32 ctlhi; +__dw32 ctlhi;/* sstat and dstat can snapshot peripheral register state. * silicon config may discard either or both... */ -u32 sstat; -u32 dstat; +__dw32 sstat; +__dw32 dstat; };struct dw_desc {/* FIRST values the hardware uses */ struct dw_lli lli; +#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO +#define lli_set (d, reg, v) ((d)->lli.reg |= cpu_to_be32(v)) +#define lli_clear (d, reg, v) ((d)->lli.reg &= ~cpu_to_be32(v)) +#define lli_read (d, reg) be32_to_cpu((d)->lli.reg) +#define lli_write (d, reg, v) ((d)->lli.reg = cpu_to_be32(v)) +#else +#define lli_set (d, reg, v) ((d)->lli.reg |= cpu_to_le32(v)) +#define lli_clear (d, reg, v) ((d)->lli.reg &= ~cpu_to_le32(v)) +#define lli_read (d, reg) le32_to_cpu((d)->lli.reg) +#define lli_write (d, reg, v) ((d)->lli.reg = cpu_to_le32(v)) +#endif +/* THEN values for driver housekeeping */ struct list_head desc_node;struct list_head tx_list;
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 2/15 dmaengine: dw: clear LLP_[SD]_EN bits in last descriptor of a chain
In reply to Andy ShevchenkoFrom: Mans Rullgard <[email protected]>
The datasheet requires that the LLP_[SD]_EN bits be cleared whenever LLP.LOC is zero, i.e. in the last descriptor of a multi-block chain. Make the driver do this.
--- drivers/dma/dw/core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/dma/dw/core.c +2 -0
@@ -763,6 +763,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,lli_set (prev, ctllo,DWC_CTLL_INT_EN ); prev->lli.llp =0 ; +lli_clear (prev, ctllo,DWC_CTLL_LLP_D_EN |DWC_CTLL_LLP_S_EN ); first->txd.flags = flags; first->total_len = len;
@@ -922,6 +923,7 @@ slave_sg_fromdev_fill_desc:lli_set (prev, ctllo,DWC_CTLL_INT_EN ); prev->lli.llp =0 ; +lli_clear (prev, ctllo,DWC_CTLL_LLP_D_EN |DWC_CTLL_LLP_S_EN ); first->total_len = total_len;return &first->txd;
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 3/15 dmaengine: dw: rename masters to reflect actual topology
In reply to Andy ShevchenkoThe source and destination masters are reflecting buses or their layers to where the different devices can be connected. The patch changes the master names to reflect which one is related to which independently on the transfer direction.
The outcome of the change is that the memory data width is now always limited by a data width of the master which is dedicated to communicate to memory.
The patch will not break anything since all current users have the same data width for all masters. Though it would be nice to revisit avr32 platforms to check what is the actual hardware topology in use there. It seems that it has one bus and two masters on it as stated by Table 8-2, that's why everything works independently on the master in use. The purpose of the sequential patch is to fix the driver for configuration of more than one bus.
The change is done in the assumption that src_master and dst_master are reflecting a connection to the memory and peripheral correspondently on all platforms except 460ex.
--- Documentation/devicetree/bindings/dma/snps-dma.txt | 4 ++-- arch/avr32/mach-at32ap/at32ap700x.c | 16 ++++++++-------- drivers/ata/sata_dwc_460ex.c | 4 ++-- drivers/dma/dw/core.c | 15 +++++++-------- drivers/dma/dw/platform.c | 12 ++++++------ drivers/dma/dw/regs.h | 4 ++-- drivers/spi/spi-pxa2xx-pci.c | 8 ++++---- drivers/tty/serial/8250/8250_pci.c | 8 ++++---- include/linux/platform_data/dma-dw.h | 8 ++++---- 9 files changed, 39 insertions(+), 40 deletions(-)
Documentation/devicetree/bindings/dma/snps-dma.txt +2 -2
@@ -47,8 +47,8 @@ The four cells in order are: 1. A phandle pointing to the DMA controller 2. The DMA request line number -3. Source master for transfers on allocated channel -4. Destination master for transfers on allocated channel +3. Memory master for transfers on allocated channel +4. Peripheral master for transfers on allocated channel Example:
arch/avr32/mach-at32ap/at32ap700x.c +8 -8
@@ -1365,8 +1365,8 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) slave->dma_dev = &dw_dmac0_device.dev; slave->src_id =0 ; slave->dst_id =1 ; - slave->src_master =1 ; - slave->dst_master =0 ; + slave->m_master =1 ; + slave->p_master =0 ; data->dma_slave = slave; data->dma_filter = at32_mci_dma_filter;
@@ -2061,16 +2061,16 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data,if (flags &AC97C_CAPTURE ) { rx_dws->dma_dev = &dw_dmac0_device.dev; rx_dws->src_id =3 ; - rx_dws->src_master =0 ; - rx_dws->dst_master =1 ; + rx_dws->m_master =0 ; + rx_dws->p_master =1 ; }/* Check if DMA slave interface for playback should be configured. */ if (flags &AC97C_PLAYBACK ) { tx_dws->dma_dev = &dw_dmac0_device.dev; tx_dws->dst_id =4 ; - tx_dws->src_master =0 ; - tx_dws->dst_master =1 ; + tx_dws->m_master =0 ; + tx_dws->p_master =1 ; } if (platform_device_add_data(pdev, data,
@@ -2141,8 +2141,8 @@ at32_add_device_abdac(unsigned int id, struct atmel_abdac_pdata *data) dws->dma_dev = &dw_dmac0_device.dev; dws->dst_id =2 ; - dws->src_master =0 ; - dws->dst_master =1 ; + dws->m_master =0 ; + dws->p_master =1 ;if (platform_device_add_data (pdev, data,sizeof (struct atmel_abdac_pdata )))
drivers/ata/sata_dwc_460ex.c +2 -2
@@ -201,8 +201,8 @@ static struct sata_dwc_host_priv host_pvt;static struct dw_dma_slave sata_dwc_dma_dws = { .src_id =0 , .dst_id =0 , - .src_master =0 , - .dst_master =1 , + .m_master =1 , + .p_master =0 , };/*
drivers/dma/dw/core.c +7 -8
@@ -50,8 +50,8 @@ | DWC_CTLL_SRC_MSIZE(_smsize) \ |DWC_CTLL_LLP_D_EN \ |DWC_CTLL_LLP_S_EN \ - |DWC_CTLL_DMS (_dwc->dst_master) \ - | DWC_CTLL_SMS(_dwc->src_master)); \ + |DWC_CTLL_DMS (_dwc->p_master) \ + | DWC_CTLL_SMS(_dwc->m_master)); \ })/*
@@ -721,8 +721,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction =DMA_MEM_TO_MEM ; - data_width = min_t(unsigned int, dw->data_width[dwc->src_master], - dw->data_width[dwc->dst_master]); + data_width = dw->data_width[dwc->m_master]; src_width = dst_width = min_t(unsigned int, data_width,dwc_fast_ffs (src | dest | len));
@@ -814,7 +813,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ?DWC_CTLL_FC (DW_DMA_FC_P_M2P ) :DWC_CTLL_FC (DW_DMA_FC_D_M2P ); - data_width = dw->data_width[dwc->src_master]; + data_width = dw->data_width[dwc->m_master];for_each_sg (sgl, sg, sg_len, i) {struct dw_desc *desc;
@@ -870,7 +869,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ?DWC_CTLL_FC (DW_DMA_FC_P_P2M ) :DWC_CTLL_FC (DW_DMA_FC_D_P2M ); - data_width = dw->data_width[dwc->dst_master]; + data_width = dw->data_width[dwc->m_master];for_each_sg (sgl, sg, sg_len, i) {struct dw_desc *desc;
@@ -948,8 +947,8 @@ bool dw_dma_filter(struct dma_chan *chan, void *param) dwc->src_id = dws->src_id; dwc->dst_id = dws->dst_id; - dwc->src_master = dws->src_master; - dwc->dst_master = dws->dst_master; + dwc->m_master = dws->m_master; + dwc->p_master = dws->p_master;return true; }
drivers/dma/dw/platform.c +6 -6
@@ -42,13 +42,13 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, slave.src_id = dma_spec->args[0 ]; slave.dst_id = dma_spec->args[0 ]; - slave.src_master = dma_spec->args[1 ]; - slave.dst_master = dma_spec->args[2 ]; + slave.m_master = dma_spec->args[1 ]; + slave.p_master = dma_spec->args[2 ];if (WARN_ON (slave.src_id >=DW_DMA_MAX_NR_REQUESTS || slave.dst_id >=DW_DMA_MAX_NR_REQUESTS || - slave.src_master >= dw->nr_masters || - slave.dst_master >= dw->nr_masters)) + slave.m_master >= dw->nr_masters || + slave.p_master >= dw->nr_masters))return NULL ;dma_cap_zero (cap);
@@ -66,8 +66,8 @@ static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) .dma_dev = dma_spec->dev, .src_id = dma_spec->slave_id, .dst_id = dma_spec->slave_id, - .src_master = 1, - .dst_master = 0, + .m_master = 1, + .p_master = 0, };return dw_dma_filter (chan, &slave);
drivers/dma/dw/regs.h +2 -2
@@ -249,8 +249,8 @@ struct dw_dma_chan {/* custom slave configuration */ u8 src_id;u8 dst_id; -u8 src_master; -u8 dst_master; +u8 m_master; +u8 p_master;/* configuration passed via .device_config */ struct dma_slave_config dma_sconfig;
drivers/spi/spi-pxa2xx-pci.c +4 -4
@@ -144,16 +144,16 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,struct dw_dma_slave *slave = c->tx_param; slave->dma_dev = &dma_dev->dev; - slave->src_master =1 ; - slave->dst_master =0 ; + slave->m_master =1 ; + slave->p_master =0 ; }if (c->rx_param) {struct dw_dma_slave *slave = c->rx_param; slave->dma_dev = &dma_dev->dev; - slave->src_master =1 ; - slave->dst_master =0 ; + slave->m_master =1 ; + slave->p_master =0 ; } spi_pdata.dma_filter = lpss_dma_filter;
drivers/tty/serial/8250/8250_pci.c +4 -4
@@ -1457,13 +1457,13 @@ byt_serial_setup(struct serial_private *priv,return -EINVAL ; } - rx_param->src_master =1 ; - rx_param->dst_master =0 ; + rx_param->m_master =1 ; + rx_param->p_master =0 ; dma->rxconf.src_maxburst =16 ; - tx_param->src_master =1 ; - tx_param->dst_master =0 ; + tx_param->m_master =1 ; + tx_param->p_master =0 ; dma->txconf.dst_maxburst =16 ;
include/linux/platform_data/dma-dw.h +4 -4
@@ -21,15 +21,15 @@* @dma_dev: required DMA master device * @src_id: src request line * @dst_id: dst request line -* @src_master: src master for transfers on allocated channel. -* @dst_master: dest master for transfers on allocated channel. +* @m_master: memory master for transfers on allocated channel +* @p_master: peripheral master for transfers on allocated channel */ struct dw_dma_slave {struct device *dma_dev;u8 src_id;u8 dst_id; -u8 src_master; -u8 dst_master; +u8 m_master; +u8 p_master; };/**
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 4/15 dmaengine: dw: set src and dst master select according to xfer direction
In reply to Andy ShevchenkoFrom: Mans Rullgard <[email protected]>
On some architectures the DMA controller can have two masters connected to different buses and thus access to memory is possible only through one and to peripheral through the other.
This patch changes the src and dst master setting to match the direction of the transfer.
--- drivers/dma/dw/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/dma/dw/core.c +6 -2
@@ -45,13 +45,17 @@DW_DMA_MSIZE_16 ; \u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \DW_DMA_MSIZE_16 ; \ +u8 _dms = (_dwc->direction ==DMA_MEM_TO_DEV ) ? \ + _dwc->p_master : _dwc->m_master; \ +u8 _sms = (_dwc->direction ==DMA_DEV_TO_MEM ) ? \ + _dwc->p_master : _dwc->m_master; \ \ (DWC_CTLL_DST_MSIZE (_dmsize) \ |DWC_CTLL_SRC_MSIZE (_smsize) \ |DWC_CTLL_LLP_D_EN \ |DWC_CTLL_LLP_S_EN \ - |DWC_CTLL_DMS (_dwc->p_master) \ - |DWC_CTLL_SMS (_dwc->m_master)); \ + |DWC_CTLL_DMS (_dms) \ + |DWC_CTLL_SMS (_sms)); \ })/*
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 5/15 dmaengine: dw: set LMS field in descriptors
In reply to Andy ShevchenkoFrom: Mans Rullgard <[email protected]>
The LMS field indicates from which master the descriptor is to be read. This patch assumes this is always the same as the memory side in a peripheral transfer which is true for all known systems.
--- drivers/dma/dw/core.c | 19 +++++++++---------- drivers/dma/dw/regs.h | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-)
drivers/dma/dw/core.c +9 -10
@@ -264,7 +264,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)dwc_initialize (dwc); -channel_writel (dwc,LLP , first->txd.phys); +channel_writel (dwc,LLP , first->txd.phys |DWC_LLP_LMS (dwc->m_master));channel_writel (dwc,CTL_LO ,DWC_CTLL_LLP_D_EN |DWC_CTLL_LLP_S_EN );channel_writel (dwc,CTL_HI ,0 );
@@ -430,7 +430,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc->residue = desc->total_len;/* Check first descriptors addr */ -if (desc->txd.phys == llp) { +if (desc->txd.phys ==DWC_LLP_LOC (llp)) {spin_unlock_irqrestore (&dwc->lock, flags);return ; }
@@ -755,7 +755,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,if (!first) { first = desc; }else { -lli_write (prev, llp, desc->txd.phys); +lli_write (prev, llp, desc->txd.phys |DWC_LLP_LMS (dwc->m_master));list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc;
@@ -852,7 +852,7 @@ slave_sg_todev_fill_desc:if (!first) { first = desc; }else { -lli_write (prev, llp, desc->txd.phys); +lli_write (prev, llp, desc->txd.phys |DWC_LLP_LMS (dwc->m_master));list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc;
@@ -907,7 +907,7 @@ slave_sg_fromdev_fill_desc:if (!first) { first = desc; }else { -lli_write (prev, llp, desc->txd.phys); +lli_write (prev, llp, desc->txd.phys |DWC_LLP_LMS (dwc->m_master));list_add_tail (&desc->desc_node, &first->tx_list); } prev = desc;
@@ -1432,13 +1432,13 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, cdesc->desc[i] = desc;if (last) -lli_write (last, llp, desc->txd.phys); +lli_write (last, llp, desc->txd.phys |DWC_LLP_LMS (dwc->m_master)); last = desc; }/* Let's make a cyclic list */ -lli_write (last, llp, cdesc->desc[0 ]->txd.phys); +lli_write (last, llp, cdesc->desc[0 ]->txd.phys |DWC_LLP_LMS (dwc->m_master)); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf %pad len %zu period %zu periods %d\n",
@@ -1640,9 +1640,8 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dwc->block_size = pdata->block_size;/* Check if channel supports multi block transfer */ -channel_writel (dwc,LLP ,0xfffffffc ); - dwc->nollp = - (channel_readl (dwc,LLP ) &0xfffffffc ) ==0 ; +channel_writel (dwc,LLP ,DWC_LLP_LOC (0xffffffff )); + dwc->nollp =DWC_LLP_LOC (channel_readl (dwc,LLP )) ==0 ;channel_writel (dwc,LLP ,0 ); } }
drivers/dma/dw/regs.h +4 -0
@@ -143,6 +143,10 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; +/* Bitfields in LLP */ +#define DWC_LLP_LMS (x) ((x) & 3)/* list master select */ +#define DWC_LLP_LOC (x) ((x) & ~3)/* next lli */ +/* Bitfields in CTL_LO */ #define DWC_CTLL_INT_EN (1 << 0)/* irqs enabled? */ #define DWC_CTLL_DST_WIDTH (n) ((n)<<1)/* bytes per element */
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 6/15 dmaengine: dw: substitute dma_read_byaddr by dma_readl_native
In reply to Andy ShevchenkoSince struct dw_dma is allocated and regs member is assigned properly we can use standard IO accessors to the DMA registers.
--- drivers/dma/dw/core.c | 8 +++----- drivers/dma/dw/regs.h | 4 ---- 2 files changed, 3 insertions(+), 9 deletions(-)
drivers/dma/dw/core.c +3 -5
@@ -1518,7 +1518,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)pm_runtime_get_sync (chip->dev);if (!pdata) { - dw_params =dma_read_byaddr (chip->regs,DW_PARAMS ); + dw_params =dma_readl (dw,DW_PARAMS );dev_dbg (chip->dev,"DW_PARAMS: 0x%08x\n" , dw_params); autocfg = dw_params >>DW_PARAMS_EN &1 ;
@@ -1618,11 +1618,9 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)/* Hardware configuration */ if (autocfg) { -unsigned int dwc_params;unsigned int r =DW_DMA_MAX_NR_CHANNELS - i -1 ; -void __iomem *addr = chip->regs + r *sizeof (u32); - - dwc_params =dma_read_byaddr (addr,DWC_PARAMS ); +void __iomem *addr = &__dw_regs (dw)->DWC_PARAMS[r]; +unsigned int dwc_params =dma_readl_native (addr);dev_dbg (chip->dev,"DWC_PARAMS[%d]: 0x%08x\n" , i, dwc_params);
drivers/dma/dw/regs.h +0 -4
@@ -114,10 +114,6 @@ struct dw_dma_regs {#define dma_writel_native writel#endif -/* To access the registers in early stage of probe */ -#define dma_read_byaddr (addr, name) \ - dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) -/* Bitfields in DW_PARAMS */ #define DW_PARAMS_NR_CHAN 8/* number of channels */ #define DW_PARAMS_NR_MASTER 11/* number of AHB masters */
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 7/15 dmaengine: dw: revisit data_width property
In reply to Andy ShevchenkoThere are several changes are done here:
- Convert the property to be in bytes
Much more convenient than keeping encoded value.
- Use one value for all AHB masters for now
It seems in practice we have no controllers where masters have different data bus width, we still might return to distinct values when there is a use case.
- Rename data_width to data-width in the device tree bindings.
- While here, replace dwc_fast_ffs() by __ffs().
--- Documentation/devicetree/bindings/dma/snps-dma.txt | 5 ++- arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +- arch/arm/boot/dts/spear13xx.dtsi | 4 +-- drivers/dma/dw/core.c | 40 +++------------------- drivers/dma/dw/platform.c | 10 +++--- drivers/dma/dw/regs.h | 2 +- include/linux/platform_data/dma-dw.h | 5 ++- 7 files changed, 18 insertions(+), 50 deletions(-)
Documentation/devicetree/bindings/dma/snps-dma.txt +2 -3
@@ -13,8 +13,7 @@ Required properties: - chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: increase from chan n->0 - block_size: Maximum block size supported by the controller -- data_width: Maximum data width supported by hardware per AHB master - (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +- data-width: Maximum data width supported by hardware (in bytes, power of 2) Optional properties:
@@ -38,7 +37,7 @@ Example: chan_allocation_order = <1>; chan_priority = <1>; block_size = <0xfff>; - data_width = <3 3>; + data-width = <8>; }; DMA clients connected to the Designware DMA controller must use the format
arch/arc/boot/dts/abilis_tb10x.dtsi +1 -1
@@ -112,7 +112,7 @@ chan_allocation_order = <0>; chan_priority = <1>; block_size = <0x7ff>; - data_width = <2>; + data-width = <4>; clocks = <&ahb_clk>; clock-names = "hclk"; };
arch/arm/boot/dts/spear13xx.dtsi +2 -2
@@ -117,7 +117,7 @@ chan_priority = <1>; block_size = <0xfff>; dma-masters = <2>; - data_width = <3 3>; + data-width = <8>; }; dma@eb000000 {
@@ -133,7 +133,7 @@ chan_allocation_order = <1>; chan_priority = <1>; block_size = <0xfff>; - data_width = <3 3>; + data-width = <8>; }; fsmc: flash@b0000000 {
drivers/dma/dw/core.c +5 -35
@@ -167,21 +167,6 @@ static void dwc_initialize(struct dw_dma_chan *dwc)/*----------------------------------------------------------------------*/ -static inline unsigned int dwc_fast_ffs (unsigned long long v) -{ -/* -* We can be a lot more clever here, but this should take care -* of the most common optimization. -*/ -if (!(v &7 )) -return 3 ; -else if (!(v &3 )) -return 2 ; -else if (!(v &1 )) -return 1 ; -return 0 ; -} -static inline void dwc_dump_chan_regs (struct dw_dma_chan *dwc) { dev_err(chan2dev(&dwc->chan),
@@ -711,7 +696,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,size_t offset;unsigned int src_width;unsigned int dst_width; -unsigned int data_width;u32 ctllo; dev_vdbg(chan2dev(chan),
@@ -725,10 +709,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction =DMA_MEM_TO_MEM ; - data_width = dw->data_width[dwc->m_master]; - - src_width = dst_width = min_t(unsigned int, data_width, -dwc_fast_ffs (src | dest | len)); + src_width = dst_width =__ffs (dw->data_width | src | dest | len); ctllo =DWC_DEFAULT_CTLLO (chan) |DWC_CTLL_DST_WIDTH (dst_width)
@@ -791,7 +772,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,dma_addr_t reg;unsigned int reg_width;unsigned int mem_width; -unsigned int data_width;unsigned int i;struct scatterlist *sg;size_t total_len =0 ;
@@ -817,8 +797,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ?DWC_CTLL_FC (DW_DMA_FC_P_M2P ) :DWC_CTLL_FC (DW_DMA_FC_D_M2P ); - data_width = dw->data_width[dwc->m_master]; -for_each_sg (sgl, sg, sg_len, i) {struct dw_desc *desc;u32 len, dlen, mem;
@@ -826,8 +804,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem =sg_dma_address (sg); len =sg_dma_len (sg); - mem_width = min_t(unsigned int, - data_width,dwc_fast_ffs (mem | len)); + mem_width =__ffs (dw->data_width | mem | len); slave_sg_todev_fill_desc: desc =dwc_desc_get (dwc);
@@ -873,8 +850,6 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ?DWC_CTLL_FC (DW_DMA_FC_P_P2M ) :DWC_CTLL_FC (DW_DMA_FC_D_P2M ); - data_width = dw->data_width[dwc->m_master]; -for_each_sg (sgl, sg, sg_len, i) {struct dw_desc *desc;u32 len, dlen, mem;
@@ -882,8 +857,7 @@ slave_sg_todev_fill_desc: mem =sg_dma_address (sg); len =sg_dma_len (sg); - mem_width = min_t(unsigned int, - data_width,dwc_fast_ffs (mem | len)); + mem_width =__ffs (dw->data_width | mem | len); slave_sg_fromdev_fill_desc: desc =dwc_desc_get (dwc);
@@ -1536,10 +1510,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)/* Get hardware configuration parameters */ pdata->nr_channels = (dw_params >>DW_PARAMS_NR_CHAN &7 ) +1 ; pdata->nr_masters = (dw_params >>DW_PARAMS_NR_MASTER &3 ) +1 ; -for (i =0 ; i < pdata->nr_masters; i++) { - pdata->data_width[i] = - (dw_params >>DW_PARAMS_DATA_WIDTH (i) &3 ) +2 ; - } + pdata->data_width =4 << (dw_params >>DW_PARAMS_DATA_WIDTH (0 ) &3 ); max_blk_size =dma_readl (dw,MAX_BLK_SIZE );/* Fill platform data with the default values */
@@ -1561,8 +1532,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)/* Get hardware configuration parameters */ dw->nr_masters = pdata->nr_masters; -for (i =0 ; i < dw->nr_masters; i++) - dw->data_width[i] = pdata->data_width[i]; + dw->data_width = pdata->data_width;/* Calculate all channel mask before DMA setup */ dw->all_chan_mask = (1 << pdata->nr_channels) -1 ;
drivers/dma/dw/platform.c +5 -5
@@ -102,8 +102,8 @@ dw_dma_parse_dt(struct platform_device *pdev) {struct device_node *np = pdev->dev.of_node;struct dw_dma_platform_data *pdata; -u32 tmp, arr[DW_DMA_MAX_NR_MASTERS ];u32 nr_channels; +u32 tmp;if (!np) {dev_err (&pdev->dev,"Missing DT data\n" );
@@ -138,10 +138,10 @@ dw_dma_parse_dt(struct platform_device *pdev) pdata->nr_masters = tmp; } -if (!of_property_read_u32_array (np,"data_width" , arr, - pdata->nr_masters)) -for (tmp =0 ; tmp < pdata->nr_masters; tmp++) - pdata->data_width[tmp] = arr[tmp]; +if (!of_property_read_u32 (np,"data-width" , &tmp)) + pdata->data_width = tmp; +else if (!of_property_read_u32 (np,"data_width" , &tmp)) + pdata->data_width =BIT (tmp);return pdata; }
drivers/dma/dw/regs.h +1 -1
@@ -285,7 +285,7 @@ struct dw_dma {/* hardware configuration */ unsigned char nr_masters; -unsigned char data_width[DW_DMA_MAX_NR_MASTERS ]; +unsigned char data_width; };static inline struct dw_dma_regs __iomem *__dw_regs (struct dw_dma *dw)
include/linux/platform_data/dma-dw.h +2 -3
@@ -42,8 +42,7 @@ struct dw_dma_slave {* @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller * @nr_masters: Number of AHB masters supported by the controller -* @data_width: Maximum data width supported by hardware per AHB master -* (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +* @data_width: Maximum data width supported by hardware (in bytes) */ struct dw_dma_platform_data {unsigned int nr_channels;
@@ -57,7 +56,7 @@ struct dw_dma_platform_data {unsigned char chan_priority;unsigned short block_size;unsigned char nr_masters; - unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; + unsigned char data_width; };#endif /* _PLATFORM_DATA_DMA_DW_H */
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 8/15 dmaengine: dw: define counter variables as unsigned int
In reply to Andy ShevchenkoThe code is fixed to satisfy a compiler otherwise we have
drivers/dma/dw/core.c: In function ‘dwc_handle_cyclic’:drivers/dma/dw/core.c:568: warning: comparison between signed and unsigned drivers/dma/dw/core.c: In function ‘dw_dma_tasklet’:drivers/dma/dw/core.c:590: warning: comparison between signed and unsigned drivers/dma/dw/core.c: In function ‘dw_dma_off’:drivers/dma/dw/core.c:1103: warning: comparison between signed and unsigned drivers/dma/dw/core.c: In function ‘dw_dma_cyclic_free’:drivers/dma/dw/core.c:1469: warning: comparison between signed and unsigned drivers/dma/dw/core.c: In function ‘dw_dma_probe’:drivers/dma/dw/core.c:1574: warning: comparison between signed and unsigned
There is no functional change.
--- drivers/dma/dw/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/dma/dw/core.c +5 -5
@@ -550,7 +550,7 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,*/ if (unlikely (status_err & dwc->mask) ||unlikely (status_xfer & dwc->mask)) { -int i; +unsigned int i; dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s interrupt, stopping DMA transfer\n",
@@ -590,7 +590,7 @@ static void dw_dma_tasklet(unsigned long data)u32 status_block;u32 status_xfer;u32 status_err; -int i; +unsigned int i; status_block =dma_readl (dw,RAW .BLOCK); status_xfer =dma_readl (dw,RAW .XFER);
@@ -1095,7 +1095,7 @@ static void dwc_issue_pending(struct dma_chan *chan)static void dw_dma_off (struct dw_dma *dw) { -int i; +unsigned int i;dma_writel (dw,CFG ,0 );
@@ -1443,7 +1443,7 @@ void dw_dma_cyclic_free(struct dma_chan *chan)struct dw_dma_chan *dwc =to_dw_dma_chan (chan);struct dw_dma *dw =to_dw_dma (dwc->chan.device);struct dw_cyclic_desc *cdesc = dwc->cdesc; -int i; +unsigned int i;unsigned long flags;dev_dbg (chan2dev (&dwc->chan),"%s\n" , __func__);
@@ -1479,8 +1479,8 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)bool autocfg = false;unsigned int dw_params;unsigned int max_blk_size =0 ; +unsigned int i;int err; -int i; dw =devm_kzalloc (chip->dev,sizeof (*dw),GFP_KERNEL );if (!dw)
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 9/15 dmaengine: dw: keep entire platform data in struct dw_dma
In reply to Andy ShevchenkoKeep the entire platform data in the struct dw_dma. It makes the driver a bit cleaner.
--- drivers/dma/dw/core.c | 31 ++++++++++++++++--------------- drivers/dma/dw/platform.c | 4 ++-- drivers/dma/dw/regs.h | 5 ++--- include/linux/platform_data/dma-dw.h | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-)
drivers/dma/dw/core.c +16 -15
@@ -709,7 +709,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction =DMA_MEM_TO_MEM ; - src_width = dst_width =__ffs (dw->data_width | src | dest | len); + src_width = dst_width =__ffs (dw->pdata->data_width | src | dest | len); ctllo =DWC_DEFAULT_CTLLO (chan) |DWC_CTLL_DST_WIDTH (dst_width)
@@ -804,7 +804,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem =sg_dma_address (sg); len =sg_dma_len (sg); - mem_width =__ffs (dw->data_width | mem | len); + mem_width =__ffs (dw->pdata->data_width | mem | len); slave_sg_todev_fill_desc: desc =dwc_desc_get (dwc);
@@ -857,7 +857,7 @@ slave_sg_todev_fill_desc: mem =sg_dma_address (sg); len =sg_dma_len (sg); - mem_width =__ffs (dw->data_width | mem | len); + mem_width =__ffs (dw->pdata->data_width | mem | len); slave_sg_fromdev_fill_desc: desc =dwc_desc_get (dwc);
@@ -1478,7 +1478,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)struct dw_dma *dw;bool autocfg = false;unsigned int dw_params; -unsigned int max_blk_size =0 ;unsigned int i;int err;
@@ -1486,6 +1485,10 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)if (!dw)return -ENOMEM ; + dw->pdata =devm_kzalloc (chip->dev,sizeof (*dw->pdata),GFP_KERNEL ); +if (!dw->pdata) +return -ENOMEM ; + dw->regs = chip->regs; chip->dw = dw;
@@ -1501,17 +1504,14 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) goto err_pdata; } - pdata =devm_kzalloc (chip->dev,sizeof (*pdata),GFP_KERNEL ); -if (!pdata) { - err = -ENOMEM ; - goto err_pdata; - } +/* Reassign the platform data pointer */ + pdata = dw->pdata;/* Get hardware configuration parameters */ pdata->nr_channels = (dw_params >>DW_PARAMS_NR_CHAN &7 ) +1 ; pdata->nr_masters = (dw_params >>DW_PARAMS_NR_MASTER &3 ) +1 ; pdata->data_width =4 << (dw_params >>DW_PARAMS_DATA_WIDTH (0 ) &3 ); - max_blk_size =dma_readl (dw,MAX_BLK_SIZE ); + pdata->block_size =dma_readl (dw,MAX_BLK_SIZE );/* Fill platform data with the default values */ pdata->is_private = true;
@@ -1521,6 +1521,11 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) }else if (pdata->nr_channels >DW_DMA_MAX_NR_CHANNELS ) { err = -EINVAL ; goto err_pdata; + }else { +memcpy (dw->pdata, pdata,sizeof (*dw->pdata)); + +/* Reassign the platform data pointer */ + pdata = dw->pdata; } dw->chan = devm_kcalloc(chip->dev, pdata->nr_channels, sizeof(*dw->chan),
@@ -1530,10 +1535,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) goto err_pdata; } -/* Get hardware configuration parameters */ - dw->nr_masters = pdata->nr_masters; - dw->data_width = pdata->data_width; -/* Calculate all channel mask before DMA setup */ dw->all_chan_mask = (1 << pdata->nr_channels) -1 ;
@@ -1601,7 +1602,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)* up to 0x0a for 4095. */ dwc->block_size = - (4 << ((max_blk_size >>4 * i) &0xf )) -1 ; + (4 << ((pdata->block_size >>4 * i) &0xf )) -1 ; dwc->nollp = (dwc_params >>DWC_PARAMS_MBLK_EN &0x1 ) ==0 ; }else {
drivers/dma/dw/platform.c +2 -2
@@ -47,8 +47,8 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,if (WARN_ON (slave.src_id >=DW_DMA_MAX_NR_REQUESTS || slave.dst_id >=DW_DMA_MAX_NR_REQUESTS || - slave.m_master >= dw->nr_masters || - slave.p_master >= dw->nr_masters)) + slave.m_master >= dw->pdata->nr_masters || + slave.p_master >= dw->pdata->nr_masters))return NULL ;dma_cap_zero (cap);
drivers/dma/dw/regs.h +2 -3
@@ -283,9 +283,8 @@ struct dw_dma {u8 all_chan_mask;u8 in_use; -/* hardware configuration */ -unsigned char nr_masters; -unsigned char data_width; +/* platform data */ +struct dw_dma_platform_data *pdata; };static inline struct dw_dma_regs __iomem *__dw_regs (struct dw_dma *dw)
include/linux/platform_data/dma-dw.h +1 -1
@@ -54,7 +54,7 @@ struct dw_dma_platform_data {#define CHAN_PRIORITY_ASCENDING 0/* chan0 highest */ #define CHAN_PRIORITY_DESCENDING 1/* chan7 highest */ unsigned char chan_priority; -unsigned short block_size; +unsigned int block_size;unsigned char nr_masters;unsigned char data_width; };
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 10/15 dmaengine: dw: pass platform data via struct dw_dma_chip
In reply to Andy ShevchenkoWe pass struct dw_dma_chip to the dw_dma_probe() anyway, thus we may use it to pass platform data as well.
--- drivers/ata/sata_dwc_460ex.c | 2 +- drivers/dma/dw/core.c | 3 ++- drivers/dma/dw/pci.c | 3 ++- drivers/dma/dw/platform.c | 3 ++- include/linux/dma/dw.h | 14 ++++++++------ sound/soc/intel/common/sst-firmware.c | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-)
drivers/ata/sata_dwc_460ex.c +1 -1
@@ -1248,7 +1248,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) hsdev->dma->dev = &ofdev->dev;/* Initialize AHB DMAC */ - err =dw_dma_probe (hsdev->dma,NULL ); + err =dw_dma_probe (hsdev->dma);if (err) goto error_dma_iomap;
drivers/dma/dw/core.c +2 -1
@@ -1473,8 +1473,9 @@ EXPORT_SYMBOL(dw_dma_cyclic_free);/*----------------------------------------------------------------------*/ -int dw_dma_probe (struct dw_dma_chip *chip,struct dw_dma_platform_data *pdata) +int dw_dma_probe (struct dw_dma_chip *chip) { +struct dw_dma_platform_data *pdata = chip->pdata;struct dw_dma *dw;bool autocfg = false;unsigned int dw_params;
drivers/dma/dw/pci.c +2 -1
@@ -49,8 +49,9 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) chip->dev = &pdev->dev; chip->regs =pcim_iomap_table (pdev)[0 ]; chip->irq = pdev->irq; + chip->pdata = pdata; - ret =dw_dma_probe (chip, pdata); + ret =dw_dma_probe (chip);if (ret)return ret;
drivers/dma/dw/platform.c +2 -1
@@ -183,6 +183,7 @@ static int dw_probe(struct platform_device *pdev) pdata =dw_dma_parse_dt (pdev); chip->dev = dev; + chip->pdata = pdata; chip->clk =devm_clk_get (chip->dev,"hclk" );if (IS_ERR (chip->clk))
@@ -193,7 +194,7 @@ static int dw_probe(struct platform_device *pdev)pm_runtime_enable (&pdev->dev); - err =dw_dma_probe (chip, pdata); + err =dw_dma_probe (chip);if (err) goto err_dw_dma_probe;
include/linux/dma/dw.h +8 -6
@@ -27,17 +27,19 @@ struct dw_dma;* @regs: memory mapped I/O space * @clk: hclk clock * @dw: struct dw_dma that is filed by dw_dma_probe() +* @pdata: pointer to platform data */ struct dw_dma_chip { -struct device *dev; -int irq; -void __iomem *regs; -struct clk *clk; -struct dw_dma *dw; +struct device *dev; +int irq; +void __iomem *regs; +struct clk *clk; +struct dw_dma *dw; +struct dw_dma_platform_data *pdata; };/* Export to the platform drivers */ -int dw_dma_probe (struct dw_dma_chip *chip,struct dw_dma_platform_data *pdata); +int dw_dma_probe (struct dw_dma_chip *chip);int dw_dma_remove (struct dw_dma_chip *chip);/* DMA API extensions */
sound/soc/intel/common/sst-firmware.c +1 -1
@@ -203,7 +203,7 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, chip->dev = dev; - err =dw_dma_probe (chip,NULL ); + err =dw_dma_probe (chip);if (err)return ERR_PTR (err);
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 11/15 dmaengine: dw: platform: use field-by-field initialization
In reply to Andy ShevchenkoThis is a simple stylish change that allows to use less lines of code.
--- drivers/dma/dw/platform.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
drivers/dma/dw/platform.c +9 -10
@@ -32,14 +32,13 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) {struct dw_dma *dw = ofdma->of_dma_data; -struct dw_dma_slave slave = { - .dma_dev = dw->dma.dev, - }; +struct dw_dma_slave slave = {0 };dma_cap_mask_t cap;if (dma_spec->args_count !=3 )return NULL ; + slave.dma_dev = dw->dma.dev; slave.src_id = dma_spec->args[0 ]; slave.dst_id = dma_spec->args[0 ]; slave.m_master = dma_spec->args[1 ];
@@ -62,13 +61,13 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,static bool dw_dma_acpi_filter (struct dma_chan *chan,void *param) {struct acpi_dma_spec *dma_spec = param; -struct dw_dma_slave slave = { - .dma_dev = dma_spec->dev, - .src_id = dma_spec->slave_id, - .dst_id = dma_spec->slave_id, - .m_master =1 , - .p_master =0 , - }; +struct dw_dma_slave slave = {0 }; + + slave.dma_dev = dma_spec->dev; + slave.src_id = dma_spec->slave_id; + slave.dst_id = dma_spec->slave_id; + slave.m_master =1 ; + slave.p_master =0 ;return dw_dma_filter (chan, &slave); }
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 12/15 dmaengine: dw: move dwc->paused to dwc->flags
In reply to Andy ShevchenkoWe have already dedicated variable for flags, therefore no need to create an additional storage for that. Convert dwc->paused to use dwc->flags.
--- drivers/dma/dw/core.c | 12 +++++------- drivers/dma/dw/regs.h | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-)
drivers/dma/dw/core.c +5 -7
@@ -979,7 +979,7 @@ static int dwc_pause(struct dma_chan *chan)while (!(channel_readl (dwc,CFG_LO ) &DWC_CFGL_FIFO_EMPTY ) && count--)udelay (2 ); - dwc->paused = true; +set_bit (DW_DMA_IS_PAUSED , &dwc->flags);spin_unlock_irqrestore (&dwc->lock, flags);
@@ -992,7 +992,7 @@ static inline void dwc_chan_resume(struct dw_dma_chan *dwc)channel_writel (dwc,CFG_LO , cfglo & ~DWC_CFGL_CH_SUSP ); - dwc->paused = false; +clear_bit (DW_DMA_IS_PAUSED , &dwc->flags); }static int dwc_resume (struct dma_chan *chan)
@@ -1000,12 +1000,10 @@ static int dwc_resume(struct dma_chan *chan)struct dw_dma_chan *dwc =to_dw_dma_chan (chan);unsigned long flags; -if (!dwc->paused) -return 0 ; -spin_lock_irqsave (&dwc->lock, flags); -dwc_chan_resume (dwc); +if (test_bit (DW_DMA_IS_PAUSED , &dwc->flags)) +dwc_chan_resume (dwc);spin_unlock_irqrestore (&dwc->lock, flags);
@@ -1074,7 +1072,7 @@ dwc_tx_status(struct dma_chan *chan,if (ret !=DMA_COMPLETE )dma_set_residue (txstate,dwc_get_residue (dwc)); -if (dwc->paused && ret ==DMA_IN_PROGRESS ) +if (test_bit (DW_DMA_IS_PAUSED , &dwc->flags) && ret ==DMA_IN_PROGRESS )return DMA_PAUSED ;return ret;
drivers/dma/dw/regs.h +1 -1
@@ -216,6 +216,7 @@ enum dw_dma_msize {enum dw_dmac_flags {DW_DMA_IS_CYCLIC =0 ,DW_DMA_IS_SOFT_LLP =1 , +DW_DMA_IS_PAUSED =2 , };struct dw_dma_chan {
@@ -224,7 +225,6 @@ struct dw_dma_chan {u8 mask;u8 priority;enum dma_transfer_direction direction; -bool paused;bool initialized;/* software emulation of the LLP transfers */
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 13/15 dmaengine: dw: move dwc->initialized to dwc->flags
In reply to Andy ShevchenkoWe have already dedicated variable for flags, therefore no need to create an additional storage for that. Covert dwc->initialized to use dwc->flags.
--- drivers/dma/dw/core.c | 8 ++++---- drivers/dma/dw/regs.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/dma/dw/core.c +4 -4
@@ -138,7 +138,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc)u32 cfghi =DWC_CFGH_FIFO_MODE ;u32 cfglo =DWC_CFGL_CH_PRIOR (dwc->priority); -if (dwc->initialized == true) +if (test_bit (DW_DMA_IS_INITIALIZED , &dwc->flags))return ;if (dws) {
@@ -162,7 +162,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc)channel_set_bit (dw,MASK .XFER, dwc->mask);channel_set_bit (dw,MASK .ERROR, dwc->mask); - dwc->initialized = true; +set_bit (DW_DMA_IS_INITIALIZED , &dwc->flags); }/*----------------------------------------------------------------------*/
@@ -1107,7 +1107,7 @@ static void dw_dma_off(struct dw_dma *dw)cpu_relax ();for (i =0 ; i < dw->dma.chancnt; i++) - dw->chan[i].initialized = false; +clear_bit (DW_DMA_IS_INITIALIZED , &dw->chan[i].flags); }static void dw_dma_on (struct dw_dma *dw)
@@ -1200,13 +1200,13 @@ static void dwc_free_chan_resources(struct dma_chan *chan)spin_lock_irqsave (&dwc->lock, flags);list_splice_init (&dwc->free_list, &list); dwc->descs_allocated =0 ; - dwc->initialized = false;/* Disable interrupts */ channel_clear_bit (dw,MASK .XFER, dwc->mask);channel_clear_bit (dw,MASK .BLOCK, dwc->mask);channel_clear_bit (dw,MASK .ERROR, dwc->mask); +clear_bit (DW_DMA_IS_INITIALIZED , &dwc->flags);spin_unlock_irqrestore (&dwc->lock, flags);/* Disable controller in case it was a last user */
drivers/dma/dw/regs.h +1 -1
@@ -217,6 +217,7 @@ enum dw_dmac_flags {DW_DMA_IS_CYCLIC =0 ,DW_DMA_IS_SOFT_LLP =1 ,DW_DMA_IS_PAUSED =2 , + DW_DMA_IS_INITIALIZED = 3, }; struct dw_dma_chan {
@@ -225,7 +226,6 @@ struct dw_dma_chan {u8 mask;u8 priority;enum dma_transfer_direction direction; -bool initialized;/* software emulation of the LLP transfers */ struct list_head *tx_node_active;
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 14/15 dmaengine: dw: move residue to a descriptor
In reply to Andy ShevchenkoResidue is a property of any active descriptor. So, any descriptor may be in different state but residue is a feature of active descriptor. Check if the asked descriptor is active and return proper residue value for it.
--- drivers/dma/dw/core.c | 60 ++++++++++++++++++++++++++++++++++----------------- drivers/dma/dw/regs.h | 2 +- 2 files changed, 41 insertions(+), 21 deletions(-)
drivers/dma/dw/core.c +40 -20
@@ -238,7 +238,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)dwc_initialize (dwc); - dwc->residue = first->total_len; + first->residue = first->total_len; dwc->tx_node_active = &first->tx_list;/* Submit first block */
@@ -369,11 +369,11 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) head = &desc->tx_list;if (active != head) { -/* Update desc to reflect last sent one */ -if (active != head->next) - desc =to_dw_desc (active->prev); - - dwc->residue -= desc->len; +/* Update residue to reflect last sent descriptor */ +if (active == head->next) + desc->residue -= desc->len; +else + desc->residue -=to_dw_desc (active->prev)->len; child =to_dw_desc (active);
@@ -388,8 +388,6 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)clear_bit (DW_DMA_IS_SOFT_LLP , &dwc->flags); } - dwc->residue =0 ; -spin_unlock_irqrestore (&dwc->lock, flags);dwc_complete_all (dw, dwc);
@@ -397,7 +395,6 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) }if (list_empty (&dwc->active_list)) { - dwc->residue =0 ;spin_unlock_irqrestore (&dwc->lock, flags);return ; }
@@ -412,7 +409,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)list_for_each_entry_safe (desc, _desc, &dwc->active_list, desc_node) {/* Initial residue value */ - dwc->residue = desc->total_len; + desc->residue = desc->total_len;/* Check first descriptors addr */ if (desc->txd.phys ==DWC_LLP_LOC (llp)) {
@@ -423,20 +420,20 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)/* Check first descriptors llp */ if (lli_read (desc, llp) == llp) {/* This one is currently in progress */ - dwc->residue -=dwc_get_sent (dwc); + desc->residue -=dwc_get_sent (dwc);spin_unlock_irqrestore (&dwc->lock, flags);return ; } - dwc->residue -= desc->len; + desc->residue -= desc->len;list_for_each_entry (child, &desc->tx_list, desc_node) {if (lli_read (child, llp) == llp) {/* Currently in progress */ - dwc->residue -=dwc_get_sent (dwc); + desc->residue -=dwc_get_sent (dwc);spin_unlock_irqrestore (&dwc->lock, flags);return ; } - dwc->residue -= child->len; + desc->residue -= child->len; }/*
@@ -1039,16 +1036,37 @@ static int dwc_terminate_all(struct dma_chan *chan)return 0 ; } -static inline u32 dwc_get_residue (struct dw_dma_chan *dwc) +static struct dw_desc *dwc_find_desc (struct dw_dma_chan *dwc,dma_cookie_t c) +{ +struct dw_desc *desc; + +list_for_each_entry (desc, &dwc->active_list, desc_node) +if (desc->txd.cookie == c) +return desc; + +return NULL ; +} + +static u32 dwc_get_residue (struct dw_dma_chan *dwc,dma_cookie_t cookie) { +struct dw_desc *desc;unsigned long flags;u32 residue;spin_lock_irqsave (&dwc->lock, flags); - residue = dwc->residue; -if (test_bit (DW_DMA_IS_SOFT_LLP , &dwc->flags) && residue) - residue -=dwc_get_sent (dwc); + desc =dwc_find_desc (dwc, cookie); +if (desc) { +if (desc ==dwc_first_active (dwc)) { + residue = desc->residue; +if (test_bit (DW_DMA_IS_SOFT_LLP , &dwc->flags) && residue) + residue -=dwc_get_sent (dwc); + }else { + residue = desc->total_len; + } + }else { + residue =0 ; + }spin_unlock_irqrestore (&dwc->lock, flags);return residue;
@@ -1069,8 +1087,10 @@ dwc_tx_status(struct dma_chan *chan,dwc_scan_descriptors (to_dw_dma (chan->device), dwc); ret =dma_cookie_status (chan, cookie, txstate); -if (ret !=DMA_COMPLETE ) -dma_set_residue (txstate,dwc_get_residue (dwc)); +if (ret ==DMA_COMPLETE ) +return ret; + +dma_set_residue (txstate,dwc_get_residue (dwc, cookie));if (test_bit (DW_DMA_IS_PAUSED , &dwc->flags) && ret ==DMA_IN_PROGRESS )return DMA_PAUSED ;
drivers/dma/dw/regs.h +1 -1
@@ -237,7 +237,6 @@ struct dw_dma_chan {struct list_head active_list;struct list_head queue;struct list_head free_list; -u32 residue;struct dw_cyclic_desc *cdesc;unsigned int descs_allocated;
@@ -351,6 +350,7 @@ struct dw_desc {struct dma_async_tx_descriptor txd;size_t len;size_t total_len; +u32 residue; };#define to_dw_desc (h) list_entry(h, struct dw_desc, desc_node)
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerPATCH v2 15/15 dmaengine: dw: set cdesc to NULL when free cyclic transfers
In reply to Andy ShevchenkoTo be sure we have the cyclic transfers already gone we set cdesc to NULL. It will prevent the double free.
--- drivers/dma/dw/core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/dma/dw/core.c +2 -0
@@ -1485,6 +1485,8 @@ void dw_dma_cyclic_free(struct dma_chan *chan)kfree (cdesc->desc);kfree (cdesc); + dwc->cdesc =NULL ; +clear_bit (DW_DMA_IS_CYCLIC , &dwc->flags); }EXPORT_SYMBOL (dw_dma_cyclic_free);
Signature · 1 line
2.7.0
Andy Shevchenko
reviewerQuoted 9 lines · Andy Shevchenko
This patch series (v1: http://www.spinics.net/lists/dmaengine/msg0793
0.html)
contains a number of mostly minor fixes and cleanups for the DW DMA driver. A
couple of them affect the DT binding so these may need to be updated to
maintain compatibility. The rest should be relatively straight- forward.
Quoted 6 lines
Changes since v1:
- zeroing structdw_dma_slavebefore use
- fall back to olddata_widthproperty if data-width is not found
- append tags for few patches
- correct title of cover letter
- rebase on top of recent linux-next
Vinod, please, postpone this series since it reveals it doesn't pass test on bare metal PowerPC board.
I'm working on it and hope will come soon with updated version.
Quoted 38 lines
Andy Shevchenko (11):
dmaengine: dw: rename masters to reflect actual topology
dmaengine: dw: substitutedma_read_byaddrbydma_readl_native
dmaengine: dw: revisitdata_widthproperty
dmaengine: dw: define counter variables as unsigned int
dmaengine: dw: keep entire platform data in structdw_dma
dmaengine: dw: pass platform data via structdw_dma_chip
dmaengine: dw: platform: use field-by-field initialization
dmaengine: dw: movedwc->pausedtodwc->flags
dmaengine: dw: movedwc->initializedtodwc->flags
dmaengine: dw: move residue to a descriptor
dmaengine: dw: set cdesc to NULL when free cyclic transfersMans Rullgard (4):
dmaengine: dw: fix byte order of hw descriptor fields
dmaengine: dw: clear LLP_[SD]_EN bits in last descriptor of a chain
dmaengine: dw: set src and dst master select according to xfer direction
dmaengine: dw: set LMS field in descriptorsDocumentation/devicetree/bindings/dma/snps-dma.txt | 9 +- arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +- arch/arm/boot/dts/spear13xx.dtsi | 4 +- arch/avr32/mach-at32ap/at32ap700x.c | 16 +- drivers/ata/sata_dwc_460ex.c | 6 +- drivers/dma/dw/core.c | 299 ++++++++++-
----------
drivers/dma/dw/pci.c | 3 +- drivers/dma/dw/platform.c | 40 +-- drivers/dma/dw/regs.h | 55 ++-- drivers/spi/spi-pxa2xx-pci.c | 8 +- drivers/tty/serial/8250/8250_pci.c | 8 +- include/linux/dma/dw.h | 14 +- include/linux/platform_data/dma-dw.h | 15 +- sound/soc/intel/common/sst-firmware.c | 2 +-14 files changed, 245 insertions(+), 236 deletions(-)
Signature · 2 lines
Andy Shevchenko <[email protected]> Intel Finland Oy