in zynq-fpga.c [384:483]
static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt)
{
struct zynq_fpga_priv *priv;
const char *why;
int err;
u32 intr_status;
unsigned long timeout;
unsigned long flags;
struct scatterlist *sg;
int i;
priv = mgr->priv;
/* The hardware can only DMA multiples of 4 bytes, and it requires the
* starting addresses to be aligned to 64 bits (UG585 pg 212).
*/
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
if ((sg->offset % 8) || (sg->length % 4)) {
dev_err(&mgr->dev,
"Invalid bitstream, chunks must be aligned\n");
return -EINVAL;
}
}
priv->dma_nelms =
dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
if (priv->dma_nelms == 0) {
dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n");
return -ENOMEM;
}
/* enable clock */
err = clk_enable(priv->clk);
if (err)
goto out_free;
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
reinit_completion(&priv->dma_done);
/* zynq_step_dma will turn on interrupts */
spin_lock_irqsave(&priv->dma_lock, flags);
priv->dma_elm = 0;
priv->cur_sg = sgt->sgl;
zynq_step_dma(priv);
spin_unlock_irqrestore(&priv->dma_lock, flags);
timeout = wait_for_completion_timeout(&priv->dma_done,
msecs_to_jiffies(DMA_TIMEOUT_MS));
spin_lock_irqsave(&priv->dma_lock, flags);
zynq_fpga_set_irq(priv, 0);
priv->cur_sg = NULL;
spin_unlock_irqrestore(&priv->dma_lock, flags);
intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
/* There doesn't seem to be a way to force cancel any DMA, so if
* something went wrong we are relying on the hardware to have halted
* the DMA before we get here, if there was we could use
* wait_for_completion_interruptible too.
*/
if (intr_status & IXR_ERROR_FLAGS_MASK) {
why = "DMA reported error";
err = -EIO;
goto out_report;
}
if (priv->cur_sg ||
!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) {
if (timeout == 0)
why = "DMA timed out";
else
why = "DMA did not complete";
err = -EIO;
goto out_report;
}
err = 0;
goto out_clk;
out_report:
dev_err(&mgr->dev,
"%s: INT_STS:0x%x CTRL:0x%x LOCK:0x%x INT_MASK:0x%x STATUS:0x%x MCTRL:0x%x\n",
why,
intr_status,
zynq_fpga_read(priv, CTRL_OFFSET),
zynq_fpga_read(priv, LOCK_OFFSET),
zynq_fpga_read(priv, INT_MASK_OFFSET),
zynq_fpga_read(priv, STATUS_OFFSET),
zynq_fpga_read(priv, MCTRL_OFFSET));
out_clk:
clk_disable(priv->clk);
out_free:
dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
return err;
}