On 13-09-18, 09:51, Masahiro Yamada wrote: > +#define UNIPHIER_MDMAC_CH_IRQ_STAT 0x010 // current hw status (RO) > +#define UNIPHIER_MDMAC_CH_IRQ_REQ 0x014 // latched STAT (WOC) > +#define UNIPHIER_MDMAC_CH_IRQ_EN 0x018 // IRQ enable mask > +#define UNIPHIER_MDMAC_CH_IRQ_DET 0x01c // REQ & EN (RO) > +#define UNIPHIER_MDMAC_CH_IRQ__ABORT BIT(13) > +#define UNIPHIER_MDMAC_CH_IRQ__DONE BIT(1) why notation if UNIPHIER_MDMAC_CH_IRQ__FOO ? > +#define UNIPHIER_MDMAC_CH_SRC_MODE 0x020 // mode of source > +#define UNIPHIER_MDMAC_CH_DEST_MODE 0x024 // mode of destination > +#define UNIPHIER_MDMAC_CH_MODE__ADDR_INC (0 << 4) > +#define UNIPHIER_MDMAC_CH_MODE__ADDR_DEC (1 << 4) > +#define UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED (2 << 4) > +#define UNIPHIER_MDMAC_CH_SRC_ADDR 0x028 // source address > +#define UNIPHIER_MDMAC_CH_DEST_ADDR 0x02c // destination address > +#define UNIPHIER_MDMAC_CH_SIZE 0x030 // transfer bytes Please use /* comment */ style for these > +/* mc->vc.lock must be held by caller */ > +static struct uniphier_mdmac_desc *__uniphier_mdmac_next_desc( > + struct uniphier_mdmac_chan *mc) this can be made to look better by: static struct uniphier_mdmac_desc * __uniphier_mdmac_next_desc(struct uniphier_mdmac_chan *mc) Btw why leading __ for function name here and other places? > +{ > + struct virt_dma_desc *vd; > + > + vd = vchan_next_desc(&mc->vc); > + if (!vd) { > + mc->md = NULL; > + return NULL; > + } > + > + list_del(&vd->node); > + > + mc->md = to_uniphier_mdmac_desc(vd); > + > + return mc->md; > +} > + > +/* mc->vc.lock must be held by caller */ > +static void __uniphier_mdmac_handle(struct uniphier_mdmac_chan *mc, > + struct uniphier_mdmac_desc *md) please align this to previous line opening brace (hint checkpatch with --strict option will give you the warning) > +static irqreturn_t uniphier_mdmac_interrupt(int irq, void *dev_id) > +{ > + struct uniphier_mdmac_chan *mc = dev_id; > + struct uniphier_mdmac_desc *md; > + irqreturn_t ret = IRQ_HANDLED; > + u32 irq_stat; > + > + spin_lock(&mc->vc.lock); > + > + irq_stat = readl(mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_DET); > + > + /* > + * Some channels share a single interrupt line. If the IRQ status is 0, > + * this is probably triggered by a different channel. > + */ > + if (!irq_stat) { > + ret = IRQ_NONE; > + goto out; > + } > + > + /* write 1 to clear */ > + writel(irq_stat, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ); > + > + /* > + * UNIPHIER_MDMAC_CH_IRQ__DONE interrupt is asserted even when the DMA > + * is aborted. To distinguish the normal completion and the abort, ^^^^ double space.. > +static int uniphier_mdmac_config(struct dma_chan *chan, > + struct dma_slave_config *config) > +{ > + /* Nothing in struct dma_slave_config is configurable. */ > + return 0; > +} I dont think config callback is mandatory, so we can drop this > +static enum dma_status uniphier_mdmac_tx_status(struct dma_chan *chan, > + dma_cookie_t cookie, > + struct dma_tx_state *txstate) > +{ > + struct virt_dma_chan *vc; > + struct virt_dma_desc *vd; > + struct uniphier_mdmac_chan *mc; > + struct uniphier_mdmac_desc *md = NULL; > + enum dma_status stat; > + unsigned long flags; > + > + stat = dma_cookie_status(chan, cookie, txstate); > + /* Return immediately if we do not need to compute the residue. */ > + if (stat == DMA_COMPLETE || !txstate) > + return stat; > + > + vc = to_virt_chan(chan); > + > + spin_lock_irqsave(&vc->lock, flags); > + > + mc = to_uniphier_mdmac_chan(vc); > + > + if (mc->md && mc->md->vd.tx.cookie == cookie) > + md = mc->md; > + > + if (!md) { > + vd = vchan_find_desc(vc, cookie); > + if (vd) > + md = to_uniphier_mdmac_desc(vd); > + } in both of these cases you are calling __uniphier_mdmac_get_residue() which reads the register and updates. But I think you should read register only in the first case when descriptor is submitted and not in latter case when descriptor is queued > +static int uniphier_mdmac_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct uniphier_mdmac_device *mdev; > + struct dma_device *ddev; > + struct resource *res; > + int nr_chans, ret, i; > + > + nr_chans = platform_irq_count(pdev); > + if (nr_chans < 0) > + return nr_chans; > + > + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); > + if (ret) > + return ret; > + > + mdev = devm_kzalloc(dev, struct_size(mdev, channels, nr_chans), > + GFP_KERNEL); > + if (!mdev) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + mdev->reg_base = devm_ioremap_resource(dev, res); > + if (IS_ERR(mdev->reg_base)) > + return PTR_ERR(mdev->reg_base); > + > + mdev->clk = devm_clk_get(dev, NULL); > + if (IS_ERR(mdev->clk)) { > + dev_err(dev, "failed to get clock\n"); > + return PTR_ERR(mdev->clk); > + } > + > + ret = clk_prepare_enable(mdev->clk); > + if (ret) > + return ret; > + > + ddev = &mdev->ddev; > + ddev->dev = dev; > + dma_cap_set(DMA_PRIVATE, ddev->cap_mask); > + ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED); > + ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED); undefined? > +static int uniphier_mdmac_remove(struct platform_device *pdev) > +{ > + struct uniphier_mdmac_device *mdev = platform_get_drvdata(pdev); > + > + of_dma_controller_free(pdev->dev.of_node); > + dma_async_device_unregister(&mdev->ddev); > + clk_disable_unprepare(mdev->clk); at this point your irq is registered and can be fired, the tasklets are not killed :( -- ~Vinod