Skip to content

Commit

Permalink
Merge tag 'dmaengine-fix-4.2-rc5' of git://git.infradead.org/users/vk…
Browse files Browse the repository at this point in the history
…oul/slave-dma

Pull dmaengine fixes from Vinod Koul:
 "We had a regression due to reuse of descriptor so we have reverted
  that.

  The rest are driver fixes:

   - at_hdmac and at_xdmac for residue, trannfer width, and channel config
   - pl330 final fix for dma fails and overflow issue
   - xgene resouce map fix
   - mv_xor big endian op fix"

* tag 'dmaengine-fix-4.2-rc5' of git://git.infradead.org/users/vkoul/slave-dma:
  Revert "dmaengine: virt-dma: don't always free descriptor upon completion"
  dmaengine: mv_xor: fix big endian operation in register mode
  dmaengine: xgene-dma: Fix the resource map to handle overlapping
  dmaengine: at_xdmac: fix transfer data width in at_xdmac_prep_slave_sg()
  dmaengine: at_hdmac: fix residue computation
  dmaengine: at_xdmac: fix bug about channel configuration
  dmaengine: pl330: Really fix choppy sound because of wrong residue calculation
  dmaengine: pl330: Fix overflow when reporting residue in memcpy
  • Loading branch information
Linus Torvalds committed Aug 1, 2015
2 parents 3270c8e + 8c8fe97 commit 3f6d9e0
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 91 deletions.
2 changes: 1 addition & 1 deletion Documentation/devicetree/bindings/dma/apm-xgene-dma.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Example:
device_type = "dma";
reg = <0x0 0x1f270000 0x0 0x10000>,
<0x0 0x1f200000 0x0 0x10000>,
<0x0 0x1b008000 0x0 0x2000>,
<0x0 0x1b000000 0x0 0x400000>,
<0x0 0x1054a000 0x0 0x100>;
interrupts = <0x0 0x82 0x4>,
<0x0 0xb8 0x4>,
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/boot/dts/apm/apm-storm.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@
device_type = "dma";
reg = <0x0 0x1f270000 0x0 0x10000>,
<0x0 0x1f200000 0x0 0x10000>,
<0x0 0x1b008000 0x0 0x2000>,
<0x0 0x1b000000 0x0 0x400000>,
<0x0 0x1054a000 0x0 0x100>;
interrupts = <0x0 0x82 0x4>,
<0x0 0xb8 0x4>,
Expand Down
132 changes: 87 additions & 45 deletions drivers/dma/at_hdmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))

#define ATC_MAX_DSCR_TRIALS 10

/*
* Initial number of descriptors to allocate for each channel. This could
* be increased during dma usage.
Expand Down Expand Up @@ -285,28 +287,19 @@ static struct at_desc *atc_get_desc_by_cookie(struct at_dma_chan *atchan,
*
* @current_len: the number of bytes left before reading CTRLA
* @ctrla: the value of CTRLA
* @desc: the descriptor containing the transfer width
*/
static inline int atc_calc_bytes_left(int current_len, u32 ctrla,
struct at_desc *desc)
static inline int atc_calc_bytes_left(int current_len, u32 ctrla)
{
return current_len - ((ctrla & ATC_BTSIZE_MAX) << desc->tx_width);
}
u32 btsize = (ctrla & ATC_BTSIZE_MAX);
u32 src_width = ATC_REG_TO_SRC_WIDTH(ctrla);

/**
* atc_calc_bytes_left_from_reg - calculates the number of bytes left according
* to the current value of CTRLA.
*
* @current_len: the number of bytes left before reading CTRLA
* @atchan: the channel to read CTRLA for
* @desc: the descriptor containing the transfer width
*/
static inline int atc_calc_bytes_left_from_reg(int current_len,
struct at_dma_chan *atchan, struct at_desc *desc)
{
u32 ctrla = channel_readl(atchan, CTRLA);

return atc_calc_bytes_left(current_len, ctrla, desc);
/*
* According to the datasheet, when reading the Control A Register
* (ctrla), the Buffer Transfer Size (btsize) bitfield refers to the
* number of transfers completed on the Source Interface.
* So btsize is always a number of source width transfers.
*/
return current_len - (btsize << src_width);
}

/**
Expand All @@ -320,7 +313,7 @@ static int atc_get_bytes_left(struct dma_chan *chan, dma_cookie_t cookie)
struct at_desc *desc_first = atc_first_active(atchan);
struct at_desc *desc;
int ret;
u32 ctrla, dscr;
u32 ctrla, dscr, trials;

/*
* If the cookie doesn't match to the currently running transfer then
Expand All @@ -346,15 +339,82 @@ static int atc_get_bytes_left(struct dma_chan *chan, dma_cookie_t cookie)
* the channel's DSCR register and compare it against the value
* of the hardware linked list structure of each child
* descriptor.
*
* The CTRLA register provides us with the amount of data
* already read from the source for the current child
* descriptor. So we can compute a more accurate residue by also
* removing the number of bytes corresponding to this amount of
* data.
*
* However, the DSCR and CTRLA registers cannot be read both
* atomically. Hence a race condition may occur: the first read
* register may refer to one child descriptor whereas the second
* read may refer to a later child descriptor in the list
* because of the DMA transfer progression inbetween the two
* reads.
*
* One solution could have been to pause the DMA transfer, read
* the DSCR and CTRLA then resume the DMA transfer. Nonetheless,
* this approach presents some drawbacks:
* - If the DMA transfer is paused, RX overruns or TX underruns
* are more likey to occur depending on the system latency.
* Taking the USART driver as an example, it uses a cyclic DMA
* transfer to read data from the Receive Holding Register
* (RHR) to avoid RX overruns since the RHR is not protected
* by any FIFO on most Atmel SoCs. So pausing the DMA transfer
* to compute the residue would break the USART driver design.
* - The atc_pause() function masks interrupts but we'd rather
* avoid to do so for system latency purpose.
*
* Then we'd rather use another solution: the DSCR is read a
* first time, the CTRLA is read in turn, next the DSCR is read
* a second time. If the two consecutive read values of the DSCR
* are the same then we assume both refers to the very same
* child descriptor as well as the CTRLA value read inbetween
* does. For cyclic tranfers, the assumption is that a full loop
* is "not so fast".
* If the two DSCR values are different, we read again the CTRLA
* then the DSCR till two consecutive read values from DSCR are
* equal or till the maxium trials is reach.
* This algorithm is very unlikely not to find a stable value for
* DSCR.
*/

ctrla = channel_readl(atchan, CTRLA);
rmb(); /* ensure CTRLA is read before DSCR */
dscr = channel_readl(atchan, DSCR);
rmb(); /* ensure DSCR is read before CTRLA */
ctrla = channel_readl(atchan, CTRLA);
for (trials = 0; trials < ATC_MAX_DSCR_TRIALS; ++trials) {
u32 new_dscr;

rmb(); /* ensure DSCR is read after CTRLA */
new_dscr = channel_readl(atchan, DSCR);

/*
* If the DSCR register value has not changed inside the
* DMA controller since the previous read, we assume
* that both the dscr and ctrla values refers to the
* very same descriptor.
*/
if (likely(new_dscr == dscr))
break;

/*
* DSCR has changed inside the DMA controller, so the
* previouly read value of CTRLA may refer to an already
* processed descriptor hence could be outdated.
* We need to update ctrla to match the current
* descriptor.
*/
dscr = new_dscr;
rmb(); /* ensure DSCR is read before CTRLA */
ctrla = channel_readl(atchan, CTRLA);
}
if (unlikely(trials >= ATC_MAX_DSCR_TRIALS))
return -ETIMEDOUT;

/* for the first descriptor we can be more accurate */
if (desc_first->lli.dscr == dscr)
return atc_calc_bytes_left(ret, ctrla, desc_first);
return atc_calc_bytes_left(ret, ctrla);

ret -= desc_first->len;
list_for_each_entry(desc, &desc_first->tx_list, desc_node) {
Expand All @@ -365,16 +425,14 @@ static int atc_get_bytes_left(struct dma_chan *chan, dma_cookie_t cookie)
}

/*
* For the last descriptor in the chain we can calculate
* For the current descriptor in the chain we can calculate
* the remaining bytes using the channel's register.
* Note that the transfer width of the first and last
* descriptor may differ.
*/
if (!desc->lli.dscr)
ret = atc_calc_bytes_left_from_reg(ret, atchan, desc);
ret = atc_calc_bytes_left(ret, ctrla);
} else {
/* single transfer */
ret = atc_calc_bytes_left_from_reg(ret, atchan, desc_first);
ctrla = channel_readl(atchan, CTRLA);
ret = atc_calc_bytes_left(ret, ctrla);
}

return ret;
Expand Down Expand Up @@ -726,7 +784,6 @@ atc_prep_dma_interleaved(struct dma_chan *chan,

desc->txd.cookie = -EBUSY;
desc->total_len = desc->len = len;
desc->tx_width = dwidth;

/* set end-of-link to the last link descriptor of list*/
set_desc_eol(desc);
Expand Down Expand Up @@ -804,10 +861,6 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
first->txd.cookie = -EBUSY;
first->total_len = len;

/* set transfer width for the calculation of the residue */
first->tx_width = src_width;
prev->tx_width = src_width;

/* set end-of-link to the last link descriptor of list*/
set_desc_eol(desc);

Expand Down Expand Up @@ -956,10 +1009,6 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
first->txd.cookie = -EBUSY;
first->total_len = total_len;

/* set transfer width for the calculation of the residue */
first->tx_width = reg_width;
prev->tx_width = reg_width;

/* first link descriptor of list is responsible of flags */
first->txd.flags = flags; /* client is in control of this ack */

Expand Down Expand Up @@ -1077,12 +1126,6 @@ atc_prep_dma_sg(struct dma_chan *chan,
desc->txd.cookie = 0;
desc->len = len;

/*
* Although we only need the transfer width for the first and
* the last descriptor, its easier to set it to all descriptors.
*/
desc->tx_width = src_width;

atc_desc_chain(&first, &prev, desc);

/* update the lengths and addresses for the next loop cycle */
Expand Down Expand Up @@ -1256,7 +1299,6 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
/* First descriptor of the chain embedds additional information */
first->txd.cookie = -EBUSY;
first->total_len = buf_len;
first->tx_width = reg_width;

return &first->txd;

Expand Down
3 changes: 1 addition & 2 deletions drivers/dma/at_hdmac_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
#define ATC_SRC_WIDTH_BYTE (0x0 << 24)
#define ATC_SRC_WIDTH_HALFWORD (0x1 << 24)
#define ATC_SRC_WIDTH_WORD (0x2 << 24)
#define ATC_REG_TO_SRC_WIDTH(r) (((r) >> 24) & 0x3)
#define ATC_DST_WIDTH_MASK (0x3 << 28) /* Destination Single Transfer Size */
#define ATC_DST_WIDTH(x) ((x) << 28)
#define ATC_DST_WIDTH_BYTE (0x0 << 28)
Expand Down Expand Up @@ -182,7 +183,6 @@ struct at_lli {
* @txd: support for the async_tx api
* @desc_node: node on the channed descriptors list
* @len: descriptor byte count
* @tx_width: transfer width
* @total_len: total transaction byte count
*/
struct at_desc {
Expand All @@ -194,7 +194,6 @@ struct at_desc {
struct dma_async_tx_descriptor txd;
struct list_head desc_node;
size_t len;
u32 tx_width;
size_t total_len;

/* Interleaved data */
Expand Down
26 changes: 14 additions & 12 deletions drivers/dma/at_xdmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,18 +359,19 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,
* descriptor view 2 since some fields of the configuration register
* depend on transfer size and src/dest addresses.
*/
if (at_xdmac_chan_is_cyclic(atchan)) {
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 if (first->lld.mbr_ubc & AT_XDMAC_MBR_UBC_NDV3) {
else if (first->lld.mbr_ubc & AT_XDMAC_MBR_UBC_NDV3)
reg = AT_XDMAC_CNDC_NDVIEW_NDV3;
} else {
/*
* No need to write AT_XDMAC_CC reg, it will be done when the
* descriptor is fecthed.
*/
else
reg = AT_XDMAC_CNDC_NDVIEW_NDV2;
}
/*
* Even if the register will be updated from the configuration in the
* descriptor when using view 2 or higher, the PROT bit won't be set
* properly. This bit can be modified only by using the channel
* configuration register.
*/
at_xdmac_chan_write(atchan, AT_XDMAC_CC, first->lld.mbr_cfg);

reg |= AT_XDMAC_CNDC_NDDUP
| AT_XDMAC_CNDC_NDSUP
Expand Down Expand Up @@ -681,15 +682,16 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
desc->lld.mbr_sa = mem;
desc->lld.mbr_da = atchan->sconfig.dst_addr;
}
desc->lld.mbr_cfg = atchan->cfg;
dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg);
dwidth = at_xdmac_get_dwidth(atchan->cfg);
fixed_dwidth = IS_ALIGNED(len, 1 << dwidth)
? at_xdmac_get_dwidth(desc->lld.mbr_cfg)
? dwidth
: 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 */
| (len >> fixed_dwidth); /* microblock length */
desc->lld.mbr_cfg = (atchan->cfg & ~AT_XDMAC_CC_DWIDTH_MASK) |
AT_XDMAC_CC_DWIDTH(fixed_dwidth);
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);
Expand Down
9 changes: 5 additions & 4 deletions drivers/dma/mv_xor.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,11 @@ static void mv_chan_set_mode(struct mv_xor_chan *chan,
config &= ~0x7;
config |= op_mode;

if (IS_ENABLED(__BIG_ENDIAN))
config |= XOR_DESCRIPTOR_SWAP;
else
config &= ~XOR_DESCRIPTOR_SWAP;
#if defined(__BIG_ENDIAN)
config |= XOR_DESCRIPTOR_SWAP;
#else
config &= ~XOR_DESCRIPTOR_SWAP;
#endif

writel_relaxed(config, XOR_CONFIG(chan));
chan->current_type = type;
Expand Down
3 changes: 2 additions & 1 deletion drivers/dma/pl330.c
Original file line number Diff line number Diff line change
Expand Up @@ -2328,7 +2328,7 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
desc->txd.callback = last->txd.callback;
desc->txd.callback_param = last->txd.callback_param;
}
last->last = false;
desc->last = false;

dma_cookie_assign(&desc->txd);

Expand Down Expand Up @@ -2623,6 +2623,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
desc->rqcfg.brst_len = 1;

desc->rqcfg.brst_len = get_burst_len(desc, len);
desc->bytes_requested = len;

desc->txd.flags = flags;

Expand Down
19 changes: 6 additions & 13 deletions drivers/dma/virt-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
spin_lock_irqsave(&vc->lock, flags);
cookie = dma_cookie_assign(tx);

list_move_tail(&vd->node, &vc->desc_submitted);
list_add_tail(&vd->node, &vc->desc_submitted);
spin_unlock_irqrestore(&vc->lock, flags);

dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
Expand Down Expand Up @@ -83,10 +83,8 @@ static void vchan_complete(unsigned long arg)
cb_data = vd->tx.callback_param;

list_del(&vd->node);
if (async_tx_test_ack(&vd->tx))
list_add(&vd->node, &vc->desc_allocated);
else
vc->desc_free(vd);

vc->desc_free(vd);

if (cb)
cb(cb_data);
Expand All @@ -98,13 +96,9 @@ void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
while (!list_empty(head)) {
struct virt_dma_desc *vd = list_first_entry(head,
struct virt_dma_desc, node);
if (async_tx_test_ack(&vd->tx)) {
list_move_tail(&vd->node, &vc->desc_allocated);
} else {
dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
list_del(&vd->node);
vc->desc_free(vd);
}
list_del(&vd->node);
dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
vc->desc_free(vd);
}
}
EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
Expand All @@ -114,7 +108,6 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
dma_cookie_init(&vc->chan);

spin_lock_init(&vc->lock);
INIT_LIST_HEAD(&vc->desc_allocated);
INIT_LIST_HEAD(&vc->desc_submitted);
INIT_LIST_HEAD(&vc->desc_issued);
INIT_LIST_HEAD(&vc->desc_completed);
Expand Down
Loading

0 comments on commit 3f6d9e0

Please sign in to comment.