summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/s3c2440/dma-s3c2440.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s3c2440/dma-s3c2440.c')
-rw-r--r--firmware/target/arm/s3c2440/dma-s3c2440.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/firmware/target/arm/s3c2440/dma-s3c2440.c b/firmware/target/arm/s3c2440/dma-s3c2440.c
new file mode 100644
index 0000000000..b83897cb22
--- /dev/null
+++ b/firmware/target/arm/s3c2440/dma-s3c2440.c
@@ -0,0 +1,207 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright 2009 by Bob Cousins
+ *
+ * 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include "config.h"
+#include "panic.h"
+#include "system.h"
+#include "mmu-arm.h"
+#include "s3c2440.h"
+#include "dma-target.h"
+#include "system-target.h"
+
+#define NUM_CHANNELS 4
+
+static int dma_used = 0;
+
+/* Status flags */
+#define STATUS_CHANNEL_ACTIVE (1<<0)
+
+struct dma_channel_state
+{
+ volatile unsigned status;
+ void (*callback)(void);
+} dma_state [NUM_CHANNELS];
+
+struct dma_channel_regs
+{
+ volatile unsigned long disrc;
+ volatile unsigned long disrcc;
+ volatile unsigned long didst;
+ volatile unsigned long didstc;
+ volatile unsigned long dcon;
+ volatile unsigned long dstat;
+ volatile unsigned long dcsrc;
+ volatile unsigned long dcdst;
+ volatile unsigned long dmasktrig;
+ volatile unsigned long reserved [7]; /* pad to 0x40 bytes */
+};
+
+
+struct dma_channel_regs *dma_regs [4] =
+ {
+ (struct dma_channel_regs *) &DISRC0,
+ (struct dma_channel_regs *) &DISRC1,
+ (struct dma_channel_regs *) &DISRC2,
+ (struct dma_channel_regs *) &DISRC3
+ }
+;
+
+
+void dma_init(void)
+{
+ /* TODO */
+
+ /* Enable interupt on DMA Finish */
+ /* Clear pending source */
+ SRCPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
+ INTPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
+
+ /* Enable interrupt in controller */
+ s3c_regclr32(&INTMOD, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
+ s3c_regclr32(&INTMSK, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
+}
+
+void dma_retain(void)
+{
+ /* TODO */
+ dma_used++;
+ if(dma_used > 0)
+ {
+ /* Enable DMA controller, clock? */
+ }
+}
+
+void dma_release(void)
+{
+ /* TODO */
+ if (dma_used > 0)
+ dma_used--;
+ if(dma_used == 0)
+ {
+ /* Disable DMA */
+ }
+}
+
+
+inline void dma_disable_channel(int channel)
+{
+ struct dma_channel_regs *regs = dma_regs [channel];
+
+ /* disable the specified channel */
+
+ /* Reset the channel */
+ regs->dmasktrig |= DMASKTRIG_STOP;
+
+ /* Wait for DMA controller to be ready */
+ while(regs->dmasktrig & DMASKTRIG_ON)
+ ;
+ while(regs->dstat & DSTAT_STAT_BUSY)
+ ;
+}
+
+void dma_enable_channel(int channel, struct dma_request *request)
+{
+ struct dma_channel_regs *regs = dma_regs [channel];
+
+ /* TODO - transfer sizes (assumes word) */
+
+ if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID)
+ panicf ("DMA: invalid channel");
+
+ /* setup a transfer on specified channel */
+ dma_disable_channel (channel);
+
+ if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR)
+ regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR;
+ else
+ regs->disrc = (unsigned long)request->source_addr;
+ regs->disrcc = request->source_control;
+
+ if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR)
+ regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR;
+ else
+ regs->didst = (unsigned long)request->dest_addr;
+ regs->didstc = request->dest_control;
+
+ regs->dcon = request->control | request->count |
+ DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL;
+
+ dma_state [channel].callback = request->callback;
+
+ /* Activate the channel */
+ invalidate_dcache_range((void *)request->dest_addr, request->count * 4);
+
+ dma_state [channel].status |= STATUS_CHANNEL_ACTIVE;
+ regs->dmasktrig = DMASKTRIG_ON;
+
+ if ((request->control & DCON_HW_SEL) == 0)
+ {
+ /* Start DMA */
+ regs->dmasktrig |= DMASKTRIG_SW_TRIG;
+ }
+
+}
+
+/* ISRs */
+inline void generic_isr (unsigned channel)
+{
+ if (dma_state [channel].status | STATUS_CHANNEL_ACTIVE)
+ {
+ if (dma_state [channel].callback)
+ /* call callback for relevant channel */
+ dma_state [channel].callback();
+
+ dma_state [channel].status &= ~STATUS_CHANNEL_ACTIVE;
+ }
+}
+
+void DMA0(void)
+{
+ generic_isr (0);
+ /* Ack the interrupt */
+ SRCPND = DMA0_MASK;
+ INTPND = DMA0_MASK;
+}
+
+void DMA1(void)
+{
+ generic_isr (1);
+ /* Ack the interrupt */
+ SRCPND = DMA1_MASK;
+ INTPND = DMA1_MASK;
+}
+
+void DMA2(void)
+{
+ generic_isr (2);
+ /* Ack the interrupt */
+ SRCPND = DMA2_MASK;
+ INTPND = DMA2_MASK;
+}
+
+void DMA3(void)
+{
+ generic_isr (3);
+ /* Ack the interrupt */
+ SRCPND = DMA3_MASK;
+ INTPND = DMA3_MASK;
+}