174 lines
		
	
	
	
		
			4.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			174 lines
		
	
	
	
		
			4.7 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Authors:	Oskar Schirmer <os@emlix.com>
							 | 
						||
| 
								 | 
							
								 *		Daniel Gloeckner <dg@emlix.com>
							 | 
						||
| 
								 | 
							
								 * (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 <linux/kernel.h>
							 | 
						||
| 
								 | 
							
								#include <linux/io.h>
							 | 
						||
| 
								 | 
							
								#include <linux/types.h>
							 | 
						||
| 
								 | 
							
								#include <linux/errno.h>
							 | 
						||
| 
								 | 
							
								#include <linux/spinlock.h>
							 | 
						||
| 
								 | 
							
								#include <asm/cacheflush.h>
							 | 
						||
| 
								 | 
							
								#include <variant/dmac.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* 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);
							 |