[PATCH v7] dma: Add Xilinx AXI Direct Memory Access Engine driver support

Jeremy Trimble jeremy.trimble at gmail.com
Fri Jun 19 09:49:25 PDT 2015


> +/**
> + * xilinx_dma_start_transfer - Starts DMA transfer
> + * @chan: Driver specific channel struct pointer
> + */
> +static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
> +{
> +       struct xilinx_dma_tx_descriptor *desc;
> +       struct xilinx_dma_tx_segment *head, *tail = NULL;
> +
> +       if (chan->err)
> +               return;
> +
> +       if (list_empty(&chan->pending_list))
> +               return;
> +
> +       if (!chan->idle)
> +               return;
> +
> +       desc = list_first_entry(&chan->pending_list,
> +                               struct xilinx_dma_tx_descriptor, node);
> +
> +       if (chan->has_sg && xilinx_dma_is_running(chan) &&
> +           !xilinx_dma_is_idle(chan)) {
> +               tail = list_entry(desc->segments.prev,
> +                                 struct xilinx_dma_tx_segment, node);
> +               dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, tail->phys);
> +               goto out_free_desc;
> +       }
> +
> +       if (chan->has_sg) {
> +               head = list_first_entry(&desc->segments,
> +                                       struct xilinx_dma_tx_segment, node);
> +               tail = list_entry(desc->segments.prev,
> +                                 struct xilinx_dma_tx_segment, node);
> +               dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC, head->phys);
> +       }
> +
> +       /* Enable interrupts */
> +       dma_ctrl_set(chan, XILINX_DMA_REG_CONTROL,
> +                    XILINX_DMA_XR_IRQ_ALL_MASK);
> +
> +       xilinx_dma_start(chan);
> +       if (chan->err)
> +               return;
> +
> +       /* Start the transfer */
> +       if (chan->has_sg) {
> +               dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, tail->phys);
> +       } else {
> +               struct xilinx_dma_tx_segment *segment;
> +               struct xilinx_dma_desc_hw *hw;
> +
> +               segment = list_first_entry(&desc->segments,
> +                                          struct xilinx_dma_tx_segment, node);
> +               hw = &segment->hw;
> +
> +               if (desc->direction == DMA_MEM_TO_DEV)
> +                       dma_ctrl_write(chan, XILINX_DMA_REG_SRCADDR,
> +                                      hw->buf_addr);
> +               else
> +                       dma_ctrl_write(chan, XILINX_DMA_REG_DSTADDR,
> +                                      hw->buf_addr);
> +
> +               /* Start the transfer */
> +               dma_ctrl_write(chan, XILINX_DMA_REG_BTT,
> +                              hw->control & XILINX_DMA_MAX_TRANS_LEN);
> +       }
> +
> +out_free_desc:
> +       list_del(&desc->node);
> +       chan->idle = false;
> +       chan->active_desc = desc;
> +}

What prevents chan->active_desc from being overwritten before the
previous descriptor is transferred to done_list.  For instance, if two
transfers are queued with issue_pending() in quick succession (such
that xilinx_dma_start_transfer() is called twice before the interrupt
for the first transfer occurs), won't the first descriptor be
overwritten and lost?



More information about the linux-arm-kernel mailing list