diff options
-rw-r--r-- | drivers/dma/ste_dma40.c | 63 |
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 | ||
306 | static struct device *chan2dev(struct d40_chan *d40c) | ||
307 | { | ||
308 | return &d40c->chan.dev->device; | ||
309 | } | ||
310 | |||
306 | static int d40_pool_lli_alloc(struct d40_desc *d40d, | 311 | static 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 | ||
709 | static 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 | |||
704 | static void d40_config_set_event(struct d40_chan *d40c, bool do_enable) | 745 | static 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); |