aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/dma/ste_dma40.c63
1 files changed, 46 insertions, 17 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 52013ed4b442..42c88fc28815 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -303,6 +303,11 @@ struct d40_reg_val {
303 unsigned int val; 303 unsigned int val;
304}; 304};
305 305
306static struct device *chan2dev(struct d40_chan *d40c)
307{
308 return &d40c->chan.dev->device;
309}
310
306static int d40_pool_lli_alloc(struct d40_desc *d40d, 311static int d40_pool_lli_alloc(struct d40_desc *d40d,
307 int lli_len, bool is_log) 312 int lli_len, bool is_log)
308{ 313{
@@ -701,17 +706,46 @@ static void d40_term_all(struct d40_chan *d40c)
701 d40c->busy = false; 706 d40c->busy = false;
702} 707}
703 708
709static void __d40_config_set_event(struct d40_chan *d40c, bool enable,
710 u32 event, int reg)
711{
712 void __iomem *addr = d40c->base->virtbase + D40_DREG_PCBASE
713 + d40c->phy_chan->num * D40_DREG_PCDELTA + reg;
714 int tries;
715
716 if (!enable) {
717 writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
718 | ~D40_EVENTLINE_MASK(event), addr);
719 return;
720 }
721
722 /*
723 * The hardware sometimes doesn't register the enable when src and dst
724 * event lines are active on the same logical channel. Retry to ensure
725 * it does. Usually only one retry is sufficient.
726 */
727 tries = 100;
728 while (--tries) {
729 writel((D40_ACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
730 | ~D40_EVENTLINE_MASK(event), addr);
731
732 if (readl(addr) & D40_EVENTLINE_MASK(event))
733 break;
734 }
735
736 if (tries != 99)
737 dev_dbg(chan2dev(d40c),
738 "[%s] workaround enable S%cLNK (%d tries)\n",
739 __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D',
740 100 - tries);
741
742 WARN_ON(!tries);
743}
744
704static void d40_config_set_event(struct d40_chan *d40c, bool do_enable) 745static void d40_config_set_event(struct d40_chan *d40c, bool do_enable)
705{ 746{
706 u32 val;
707 unsigned long flags; 747 unsigned long flags;
708 748
709 /* Notice, that disable requires the physical channel to be stopped */
710 if (do_enable)
711 val = D40_ACTIVATE_EVENTLINE;
712 else
713 val = D40_DEACTIVATE_EVENTLINE;
714
715 spin_lock_irqsave(&d40c->phy_chan->lock, flags); 749 spin_lock_irqsave(&d40c->phy_chan->lock, flags);
716 750
717 /* Enable event line connected to device (or memcpy) */ 751 /* Enable event line connected to device (or memcpy) */
@@ -719,20 +753,15 @@ static void d40_config_set_event(struct d40_chan *d40c, bool do_enable)
719 (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) { 753 (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) {
720 u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type); 754 u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
721 755
722 writel((val << D40_EVENTLINE_POS(event)) | 756 __d40_config_set_event(d40c, do_enable, event,
723 ~D40_EVENTLINE_MASK(event), 757 D40_CHAN_REG_SSLNK);
724 d40c->base->virtbase + D40_DREG_PCBASE +
725 d40c->phy_chan->num * D40_DREG_PCDELTA +
726 D40_CHAN_REG_SSLNK);
727 } 758 }
759
728 if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) { 760 if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) {
729 u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type); 761 u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
730 762
731 writel((val << D40_EVENTLINE_POS(event)) | 763 __d40_config_set_event(d40c, do_enable, event,
732 ~D40_EVENTLINE_MASK(event), 764 D40_CHAN_REG_SDLNK);
733 d40c->base->virtbase + D40_DREG_PCBASE +
734 d40c->phy_chan->num * D40_DREG_PCDELTA +
735 D40_CHAN_REG_SDLNK);
736 } 765 }
737 766
738 spin_unlock_irqrestore(&d40c->phy_chan->lock, flags); 767 spin_unlock_irqrestore(&d40c->phy_chan->lock, flags);