aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2011-01-25 05:18:05 -0500
committerDan Williams <dan.j.williams@intel.com>2011-01-31 01:27:15 -0500
commit262d2915d4f11e5e78e432ab68f0ee034ef3f75f (patch)
tree9c14e8b8518fbd377ba96826bcb03bb02f89ed34 /drivers
parentcb9ab2d8e4661c811d5e9a8e687b6f736690c90e (diff)
dma40: ensure event lines get enabled
The controller sometimes fails to register the enable of the event line when both src and dst event lines are used on the same logical channel. Implement the recommended software workaround, which is to retry the write until it works. Acked-by: Per Forlin <per.forlin@stericsson.com> Acked-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers')
-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);