diff options
Diffstat (limited to 'drivers/dma/xilinx/xilinx_dpdma.c')
-rw-r--r-- | drivers/dma/xilinx/xilinx_dpdma.c | 75 |
1 files changed, 56 insertions, 19 deletions
diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index 70b29bd079c9..b280a53e8570 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -113,6 +113,7 @@ #define XILINX_DPDMA_CH_VDO 0x020 #define XILINX_DPDMA_CH_PYLD_SZ 0x024 #define XILINX_DPDMA_CH_DESC_ID 0x028 +#define XILINX_DPDMA_CH_DESC_ID_MASK GENMASK(15, 0) /* DPDMA descriptor fields */ #define XILINX_DPDMA_DESC_CONTROL_PREEMBLE 0xa5 @@ -530,7 +531,7 @@ static void xilinx_dpdma_sw_desc_set_dma_addrs(struct xilinx_dpdma_device *xdev, for (i = 1; i < num_src_addr; i++) { u32 *addr = &hw_desc->src_addr2; - addr[i-1] = lower_32_bits(dma_addr[i]); + addr[i - 1] = lower_32_bits(dma_addr[i]); if (xdev->ext_addr) { u32 *addr_ext = &hw_desc->addr_ext_23; @@ -702,8 +703,9 @@ xilinx_dpdma_chan_prep_interleaved_dma(struct xilinx_dpdma_chan *chan, size_t stride = hsize + xt->sgl[0].icg; if (!IS_ALIGNED(xt->src_start, XILINX_DPDMA_ALIGN_BYTES)) { - dev_err(chan->xdev->dev, "buffer should be aligned at %d B\n", - XILINX_DPDMA_ALIGN_BYTES); + dev_err(chan->xdev->dev, + "chan%u: buffer should be aligned at %d B\n", + chan->id, XILINX_DPDMA_ALIGN_BYTES); return NULL; } @@ -866,7 +868,8 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan) * will be used, but it should be enough. */ list_for_each_entry(sw_desc, &desc->descriptors, node) - sw_desc->hw.desc_id = desc->vdesc.tx.cookie; + sw_desc->hw.desc_id = desc->vdesc.tx.cookie + & XILINX_DPDMA_CH_DESC_ID_MASK; sw_desc = list_first_entry(&desc->descriptors, struct xilinx_dpdma_sw_desc, node); @@ -915,7 +918,7 @@ static u32 xilinx_dpdma_chan_ostand(struct xilinx_dpdma_chan *chan) } /** - * xilinx_dpdma_chan_no_ostand - Notify no outstanding transaction event + * xilinx_dpdma_chan_notify_no_ostand - Notify no outstanding transaction event * @chan: DPDMA channel * * Notify waiters for no outstanding event, so waiters can stop the channel @@ -934,7 +937,9 @@ static int xilinx_dpdma_chan_notify_no_ostand(struct xilinx_dpdma_chan *chan) cnt = xilinx_dpdma_chan_ostand(chan); if (cnt) { - dev_dbg(chan->xdev->dev, "%d outstanding transactions\n", cnt); + dev_dbg(chan->xdev->dev, + "chan%u: %d outstanding transactions\n", + chan->id, cnt); return -EWOULDBLOCK; } @@ -970,8 +975,8 @@ static int xilinx_dpdma_chan_wait_no_ostand(struct xilinx_dpdma_chan *chan) return 0; } - dev_err(chan->xdev->dev, "not ready to stop: %d trans\n", - xilinx_dpdma_chan_ostand(chan)); + dev_err(chan->xdev->dev, "chan%u: not ready to stop: %d trans\n", + chan->id, xilinx_dpdma_chan_ostand(chan)); if (ret == 0) return -ETIMEDOUT; @@ -1005,8 +1010,8 @@ static int xilinx_dpdma_chan_poll_no_ostand(struct xilinx_dpdma_chan *chan) return 0; } - dev_err(chan->xdev->dev, "not ready to stop: %d trans\n", - xilinx_dpdma_chan_ostand(chan)); + dev_err(chan->xdev->dev, "chan%u: not ready to stop: %d trans\n", + chan->id, xilinx_dpdma_chan_ostand(chan)); return -ETIMEDOUT; } @@ -1060,7 +1065,8 @@ static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan) vchan_cyclic_callback(&active->vdesc); else dev_warn(chan->xdev->dev, - "DONE IRQ with no active descriptor!\n"); + "chan%u: DONE IRQ with no active descriptor!\n", + chan->id); spin_unlock_irqrestore(&chan->lock, flags); } @@ -1086,13 +1092,18 @@ static void xilinx_dpdma_chan_vsync_irq(struct xilinx_dpdma_chan *chan) if (!chan->running || !pending) goto out; - desc_id = dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_ID); + desc_id = dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_ID) + & XILINX_DPDMA_CH_DESC_ID_MASK; /* If the retrigger raced with vsync, retry at the next frame. */ sw_desc = list_first_entry(&pending->descriptors, struct xilinx_dpdma_sw_desc, node); - if (sw_desc->hw.desc_id != desc_id) + if (sw_desc->hw.desc_id != desc_id) { + dev_dbg(chan->xdev->dev, + "chan%u: vsync race lost (%u != %u), retrying\n", + chan->id, sw_desc->hw.desc_id, desc_id); goto out; + } /* * Complete the active descriptor, if any, promote the pending @@ -1148,10 +1159,12 @@ static void xilinx_dpdma_chan_handle_err(struct xilinx_dpdma_chan *chan) spin_lock_irqsave(&chan->lock, flags); - dev_dbg(xdev->dev, "cur desc addr = 0x%04x%08x\n", + dev_dbg(xdev->dev, "chan%u: cur desc addr = 0x%04x%08x\n", + chan->id, dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDRE), dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDR)); - dev_dbg(xdev->dev, "cur payload addr = 0x%04x%08x\n", + dev_dbg(xdev->dev, "chan%u: cur payload addr = 0x%04x%08x\n", + chan->id, dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDRE), dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDR)); @@ -1167,7 +1180,8 @@ static void xilinx_dpdma_chan_handle_err(struct xilinx_dpdma_chan *chan) xilinx_dpdma_chan_dump_tx_desc(chan, active); if (active->error) - dev_dbg(xdev->dev, "repeated error on desc\n"); + dev_dbg(xdev->dev, "chan%u: repeated error on desc\n", + chan->id); /* Reschedule if there's no new descriptor */ if (!chan->desc.pending && @@ -1232,7 +1246,8 @@ static int xilinx_dpdma_alloc_chan_resources(struct dma_chan *dchan) align, 0); if (!chan->desc_pool) { dev_err(chan->xdev->dev, - "failed to allocate a descriptor pool\n"); + "chan%u: failed to allocate a descriptor pool\n", + chan->id); return -ENOMEM; } @@ -1459,7 +1474,7 @@ static void xilinx_dpdma_enable_irq(struct xilinx_dpdma_device *xdev) */ static void xilinx_dpdma_disable_irq(struct xilinx_dpdma_device *xdev) { - dpdma_write(xdev->reg, XILINX_DPDMA_IDS, XILINX_DPDMA_INTR_ERR_ALL); + dpdma_write(xdev->reg, XILINX_DPDMA_IDS, XILINX_DPDMA_INTR_ALL); dpdma_write(xdev->reg, XILINX_DPDMA_EIDS, XILINX_DPDMA_EINTR_ALL); } @@ -1585,7 +1600,7 @@ static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct xilinx_dpdma_device *xdev = ofdma->of_dma_data; - uint32_t chan_id = dma_spec->args[0]; + u32 chan_id = dma_spec->args[0]; if (chan_id >= ARRAY_SIZE(xdev->chan)) return NULL; @@ -1596,6 +1611,26 @@ static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec, return dma_get_slave_channel(&xdev->chan[chan_id]->vchan.chan); } +static void dpdma_hw_init(struct xilinx_dpdma_device *xdev) +{ + unsigned int i; + void __iomem *reg; + + /* Disable all interrupts */ + xilinx_dpdma_disable_irq(xdev); + + /* Stop all channels */ + for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) { + reg = xdev->reg + XILINX_DPDMA_CH_BASE + + XILINX_DPDMA_CH_OFFSET * i; + dpdma_clr(reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_ENABLE); + } + + /* Clear the interrupt status registers */ + dpdma_write(xdev->reg, XILINX_DPDMA_ISR, XILINX_DPDMA_INTR_ALL); + dpdma_write(xdev->reg, XILINX_DPDMA_EISR, XILINX_DPDMA_EINTR_ALL); +} + static int xilinx_dpdma_probe(struct platform_device *pdev) { struct xilinx_dpdma_device *xdev; @@ -1622,6 +1657,8 @@ static int xilinx_dpdma_probe(struct platform_device *pdev) if (IS_ERR(xdev->reg)) return PTR_ERR(xdev->reg); + dpdma_hw_init(xdev); + xdev->irq = platform_get_irq(pdev, 0); if (xdev->irq < 0) { dev_err(xdev->dev, "failed to get platform irq\n"); |