When multiple channels operate simultaneously, the common register shared by all channels may be modified to an unexpected value, hence a spinlock is added to avoid this. Signed-off-by: Libing Lei <libing.lei@xxxxxxxxxxxxxxx> --- .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 20 +++++++++++++++++++ drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index bf85aa097..7aa53293c 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -108,37 +108,49 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan, static inline void axi_dma_disable(struct axi_dma_chip *chip) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chip->lock, flags); val = axi_dma_ioread32(chip, DMAC_CFG); val &= ~DMAC_EN_MASK; axi_dma_iowrite32(chip, DMAC_CFG, val); + spin_unlock_irqrestore(&chip->lock, flags); } static inline void axi_dma_enable(struct axi_dma_chip *chip) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chip->lock, flags); val = axi_dma_ioread32(chip, DMAC_CFG); val |= DMAC_EN_MASK; axi_dma_iowrite32(chip, DMAC_CFG, val); + spin_unlock_irqrestore(&chip->lock, flags); } static inline void axi_dma_irq_disable(struct axi_dma_chip *chip) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chip->lock, flags); val = axi_dma_ioread32(chip, DMAC_CFG); val &= ~INT_EN_MASK; axi_dma_iowrite32(chip, DMAC_CFG, val); + spin_unlock_irqrestore(&chip->lock, flags); } static inline void axi_dma_irq_enable(struct axi_dma_chip *chip) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chip->lock, flags); val = axi_dma_ioread32(chip, DMAC_CFG); val |= INT_EN_MASK; axi_dma_iowrite32(chip, DMAC_CFG, val); + spin_unlock_irqrestore(&chip->lock, flags); } static inline void axi_chan_irq_disable(struct axi_dma_chan *chan, u32 irq_mask) @@ -177,7 +189,9 @@ static inline u32 axi_chan_irq_read(struct axi_dma_chan *chan) static inline void axi_chan_disable(struct axi_dma_chan *chan) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chan->chip->lock, flags); val = axi_dma_ioread32(chan->chip, DMAC_CHEN); val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT); if (chan->chip->dw->hdata->reg_map_8_channels) @@ -185,12 +199,15 @@ static inline void axi_chan_disable(struct axi_dma_chan *chan) else val |= BIT(chan->id) << DMAC_CHAN_EN2_WE_SHIFT; axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); + spin_unlock_irqrestore(&chan->chip->lock, flags); } static inline void axi_chan_enable(struct axi_dma_chan *chan) { u32 val; + unsigned long flags; + spin_lock_irqsave(&chan->chip->lock, flags); val = axi_dma_ioread32(chan->chip, DMAC_CHEN); if (chan->chip->dw->hdata->reg_map_8_channels) val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT | @@ -199,6 +216,7 @@ static inline void axi_chan_enable(struct axi_dma_chan *chan) val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT | BIT(chan->id) << DMAC_CHAN_EN2_WE_SHIFT; axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); + spin_unlock_irqrestore(&chan->chip->lock, flags); } static inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan) @@ -1420,6 +1438,8 @@ static int dw_probe(struct platform_device *pdev) if (ret) return ret; + spin_lock_init(&chip->lock); + dw->chan = devm_kcalloc(chip->dev, hdata->nr_channels, sizeof(*dw->chan), GFP_KERNEL); if (!dw->chan) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index e9d5eb0fd..4a8db437e 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -70,6 +70,7 @@ struct axi_dma_chip { struct clk *core_clk; struct clk *cfgr_clk; struct dw_axi_dma *dw; + spinlock_t lock; }; /* LLI == Linked List Item */ -- 2.17.1