/* linux/arch/arm/plat-s3c24xx/dma.c * * Copyright 2003-2006 Simtec Electronics * Ben Dooks * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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; }