|  | @@ -0,0 +1,198 @@
 | 
	
		
			
				|  |  | +/* linux/arch/arm/plat-s3c24xx/dma.c
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Copyright 2003-2006 Simtec Electronics
 | 
	
		
			
				|  |  | + *	Ben Dooks <ben@simtec.co.uk>
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * S3C2410 DMA core
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * http://armlinux.simtec.co.uk/
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This program is free software; you can redistribute it and/or modify
 | 
	
		
			
				|  |  | + * it under the terms of the GNU General Public License version 2 as
 | 
	
		
			
				|  |  | + * published by the Free Software Foundation.
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef CONFIG_S3C2410_DMA_DEBUG
 | 
	
		
			
				|  |  | +#define DEBUG
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <linux/module.h>
 | 
	
		
			
				|  |  | +#include <linux/init.h>
 | 
	
		
			
				|  |  | +#include <linux/sched.h>
 | 
	
		
			
				|  |  | +#include <linux/spinlock.h>
 | 
	
		
			
				|  |  | +#include <linux/interrupt.h>
 | 
	
		
			
				|  |  | +#include <linux/syscore_ops.h>
 | 
	
		
			
				|  |  | +#include <linux/slab.h>
 | 
	
		
			
				|  |  | +#include <linux/errno.h>
 | 
	
		
			
				|  |  | +#include <linux/io.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <asm/irq.h>
 | 
	
		
			
				|  |  | +#include <mach/hardware.h>
 | 
	
		
			
				|  |  | +#include <mach/dma.h>
 | 
	
		
			
				|  |  | +#include <mach/map.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <plat/dma-s3c24xx.h>
 | 
	
		
			
				|  |  | +#include <plat/regs-dma.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* io map for dma */
 | 
	
		
			
				|  |  | +static void __iomem *dma_base;
 | 
	
		
			
				|  |  | +static struct kmem_cache *dma_kmem;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int dma_channels;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static struct s3c24xx_dma_selection dma_sel;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* debugging functions */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define BUF_MAGIC (0xcafebabe)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define dmawarn(fmt...) printk(KERN_DEBUG fmt)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define dma_regaddr(chan, reg) ((chan)->regs + (reg))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if 1
 | 
	
		
			
				|  |  | +#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +static inline void
 | 
	
		
			
				|  |  | +dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg);
 | 
	
		
			
				|  |  | +	writel(val, dma_regaddr(chan, reg));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* captured register state for debug */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct s3c2410_dma_regstate {
 | 
	
		
			
				|  |  | +	unsigned long         dcsrc;
 | 
	
		
			
				|  |  | +	unsigned long         disrc;
 | 
	
		
			
				|  |  | +	unsigned long         dstat;
 | 
	
		
			
				|  |  | +	unsigned long         dcon;
 | 
	
		
			
				|  |  | +	unsigned long         dmsktrig;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef CONFIG_S3C2410_DMA_DEBUG
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* dmadbg_showregs
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * simple debug routine to print the current state of the dma registers
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	regs->dcsrc    = dma_rdreg(chan, S3C2410_DMA_DCSRC);
 | 
	
		
			
				|  |  | +	regs->disrc    = dma_rdreg(chan, S3C2410_DMA_DISRC);
 | 
	
		
			
				|  |  | +	regs->dstat    = dma_rdreg(chan, S3C2410_DMA_DSTAT);
 | 
	
		
			
				|  |  | +	regs->dcon     = dma_rdreg(chan, S3C2410_DMA_DCON);
 | 
	
		
			
				|  |  | +	regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan,
 | 
	
		
			
				|  |  | +		 struct s3c2410_dma_regstate *regs)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
 | 
	
		
			
				|  |  | +	       chan->number, fname, line,
 | 
	
		
			
				|  |  | +	       regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
 | 
	
		
			
				|  |  | +	       regs->dcon);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct s3c2410_dma_regstate state;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dmadbg_capture(chan, &state);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n",
 | 
	
		
			
				|  |  | +	       chan->number, fname, line, chan->load_state,
 | 
	
		
			
				|  |  | +	       chan->curr, chan->next, chan->end);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dmadbg_dumpregs(fname, line, chan, &state);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct s3c2410_dma_regstate state;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dmadbg_capture(chan, &state);
 | 
	
		
			
				|  |  | +	dmadbg_dumpregs(fname, line, chan, &state);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define dbg_showregs(chan) dmadbg_showregs(__func__, __LINE__, (chan))
 | 
	
		
			
				|  |  | +#define dbg_showchan(chan) dmadbg_showchan(__func__, __LINE__, (chan))
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +#define dbg_showregs(chan) do { } while(0)
 | 
	
		
			
				|  |  | +#define dbg_showchan(chan) do { } while(0)
 | 
	
		
			
				|  |  | +#endif /* CONFIG_S3C2410_DMA_DEBUG */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* s3c2410_dma_stats_timeout
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Update DMA stats from timeout info
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	if (stats == NULL)
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (val > stats->timeout_longest)
 | 
	
		
			
				|  |  | +		stats->timeout_longest = val;
 | 
	
		
			
				|  |  | +	if (val < stats->timeout_shortest)
 | 
	
		
			
				|  |  | +		stats->timeout_shortest = val;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	stats->timeout_avg += val;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* s3c2410_dma_waitforload
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * wait for the DMA engine to load a buffer, and update the state accordingly
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int
 | 
	
		
			
				|  |  | +s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int timeout = chan->load_timeout;
 | 
	
		
			
				|  |  | +	int took;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->load_state != S3C2410_DMALOAD_1LOADED) {
 | 
	
		
			
				|  |  | +		printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line);
 | 
	
		
			
				|  |  | +		return 0;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->stats != NULL)
 | 
	
		
			
				|  |  | +		chan->stats->loads++;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	while (--timeout > 0) {
 | 
	
		
			
				|  |  | +		if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) {
 | 
	
		
			
				|  |  | +			took = chan->load_timeout - timeout;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			s3c2410_dma_stats_timeout(chan->stats, took);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			switch (chan->load_state) {
 | 
	
		
			
				|  |  | +			case S3C2410_DMALOAD_1LOADED:
 | 
	
		
			
				|  |  | +				chan->load_state = S3C2410_DMALOAD_1RUNNING;
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			default:
 | 
	
		
			
				|  |  | +				printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			return 1;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->stats != NULL) {
 | 
	
		
			
				|  |  | +		chan->stats->timeout_failed++;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 |