/* * Authors: Oskar Schirmer * Daniel Gloeckner * (c) 2008 emlix GmbH http://www.emlix.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include /* DMA engine lookup */ struct s6dmac_ctrl s6dmac_ctrl[S6_DMAC_NB]; /* DMA control, per engine */ void s6dmac_put_fifo_cache(u32 dmac, int chan, u32 src, u32 dst, u32 size) { if (xtensa_need_flush_dma_source(src)) { u32 base = src; u32 span = size; u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK); if (chunk && (size > chunk)) { s32 skip = readl(DMA_CHNL(dmac, chan) + S6_DMA_SRCSKIP); u32 gaps = (size+chunk-1)/chunk - 1; if (skip >= 0) { span += gaps * skip; } else if (-skip > chunk) { s32 decr = gaps * (chunk + skip); base += decr; span = chunk - decr; } else { span = max(span + gaps * skip, (chunk + skip) * gaps - skip); } } flush_dcache_unaligned(base, span); } if (xtensa_need_invalidate_dma_destination(dst)) { u32 base = dst; u32 span = size; u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK); if (chunk && (size > chunk)) { s32 skip = readl(DMA_CHNL(dmac, chan) + S6_DMA_DSTSKIP); u32 gaps = (size+chunk-1)/chunk - 1; if (skip >= 0) { span += gaps * skip; } else if (-skip > chunk) { s32 decr = gaps * (chunk + skip); base += decr; span = chunk - decr; } else { span = max(span + gaps * skip, (chunk + skip) * gaps - skip); } } invalidate_dcache_unaligned(base, span); } s6dmac_put_fifo(dmac, chan, src, dst, size); } void s6dmac_disable_error_irqs(u32 dmac, u32 mask) { unsigned long flags; spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock; spin_lock_irqsave(spinl, flags); _s6dmac_disable_error_irqs(dmac, mask); spin_unlock_irqrestore(spinl, flags); } u32 s6dmac_int_sources(u32 dmac, u32 channel) { u32 mask, ret, tmp; mask = 1 << channel; tmp = readl(dmac + S6_DMA_TERMCNTIRQSTAT); tmp &= mask; writel(tmp, dmac + S6_DMA_TERMCNTIRQCLR); ret = tmp >> channel; tmp = readl(dmac + S6_DMA_PENDCNTIRQSTAT); tmp &= mask; writel(tmp, dmac + S6_DMA_PENDCNTIRQCLR); ret |= (tmp >> channel) << 1; tmp = readl(dmac + S6_DMA_LOWWMRKIRQSTAT); tmp &= mask; writel(tmp, dmac + S6_DMA_LOWWMRKIRQCLR); ret |= (tmp >> channel) << 2; tmp = readl(dmac + S6_DMA_INTRAW0); tmp &= (mask << S6_DMA_INT0_OVER) | (mask << S6_DMA_INT0_UNDER); writel(tmp, dmac + S6_DMA_INTCLEAR0); if (tmp & (mask << S6_DMA_INT0_UNDER)) ret |= 1 << 3; if (tmp & (mask << S6_DMA_INT0_OVER)) ret |= 1 << 4; tmp = readl(dmac + S6_DMA_MASTERERRINFO); mask <<= S6_DMA_INT1_CHANNEL; if (((tmp >> S6_DMA_MASTERERR_CHAN(0)) & S6_DMA_MASTERERR_CHAN_MASK) == channel) mask |= 1 << S6_DMA_INT1_MASTER; if (((tmp >> S6_DMA_MASTERERR_CHAN(1)) & S6_DMA_MASTERERR_CHAN_MASK) == channel) mask |= 1 << (S6_DMA_INT1_MASTER + 1); if (((tmp >> S6_DMA_MASTERERR_CHAN(2)) & S6_DMA_MASTERERR_CHAN_MASK) == channel) mask |= 1 << (S6_DMA_INT1_MASTER + 2); tmp = readl(dmac + S6_DMA_INTRAW1) & mask; writel(tmp, dmac + S6_DMA_INTCLEAR1); ret |= ((tmp >> channel) & 1) << 5; ret |= ((tmp >> S6_DMA_INT1_MASTER) & S6_DMA_INT1_MASTER_MASK) << 6; return ret; } void s6dmac_release_chan(u32 dmac, int chan) { if (chan >= 0) s6dmac_disable_chan(dmac, chan); } /* global init */ static inline void __init dmac_init(u32 dmac, u8 chan_nb) { s6dmac_ctrl[S6_DMAC_INDEX(dmac)].dmac = dmac; spin_lock_init(&s6dmac_ctrl[S6_DMAC_INDEX(dmac)].lock); s6dmac_ctrl[S6_DMAC_INDEX(dmac)].chan_nb = chan_nb; writel(S6_DMA_INT1_MASTER_MASK << S6_DMA_INT1_MASTER, dmac + S6_DMA_INTCLEAR1); } static inline void __init dmac_master(u32 dmac, u32 m0start, u32 m0end, u32 m1start, u32 m1end) { writel(m0start, dmac + S6_DMA_MASTER0START); writel(m0end - 1, dmac + S6_DMA_MASTER0END); writel(m1start, dmac + S6_DMA_MASTER1START); writel(m1end - 1, dmac + S6_DMA_MASTER1END); } static void __init s6_dmac_init(void) { dmac_init(S6_REG_LMSDMA, S6_LMSDMA_NB); dmac_master(S6_REG_LMSDMA, S6_MEM_DDR, S6_MEM_PCIE_APER, S6_MEM_EFI, S6_MEM_GMAC); dmac_init(S6_REG_NIDMA, S6_NIDMA_NB); dmac_init(S6_REG_DPDMA, S6_DPDMA_NB); dmac_master(S6_REG_DPDMA, S6_MEM_DDR, S6_MEM_PCIE_APER, S6_REG_DP, S6_REG_DPDMA); dmac_init(S6_REG_HIFDMA, S6_HIFDMA_NB); dmac_master(S6_REG_HIFDMA, S6_MEM_GMAC, S6_MEM_PCIE_CFG, S6_MEM_PCIE_APER, S6_MEM_AUX); } arch_initcall(s6_dmac_init);