ARM: use BX when branch to an address in register
[kugel-rb.git] / firmware / target / arm / s3c2440 / dma-s3c2440.c
blobb83897cb228df00f03b61313eb67264535396ce8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright 2009 by Bob Cousins
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdbool.h>
23 #include "config.h"
24 #include "panic.h"
25 #include "system.h"
26 #include "mmu-arm.h"
27 #include "s3c2440.h"
28 #include "dma-target.h"
29 #include "system-target.h"
31 #define NUM_CHANNELS 4
33 static int dma_used = 0;
35 /* Status flags */
36 #define STATUS_CHANNEL_ACTIVE (1<<0)
38 struct dma_channel_state
40 volatile unsigned status;
41 void (*callback)(void);
42 } dma_state [NUM_CHANNELS];
44 struct dma_channel_regs
46 volatile unsigned long disrc;
47 volatile unsigned long disrcc;
48 volatile unsigned long didst;
49 volatile unsigned long didstc;
50 volatile unsigned long dcon;
51 volatile unsigned long dstat;
52 volatile unsigned long dcsrc;
53 volatile unsigned long dcdst;
54 volatile unsigned long dmasktrig;
55 volatile unsigned long reserved [7]; /* pad to 0x40 bytes */
59 struct dma_channel_regs *dma_regs [4] =
61 (struct dma_channel_regs *) &DISRC0,
62 (struct dma_channel_regs *) &DISRC1,
63 (struct dma_channel_regs *) &DISRC2,
64 (struct dma_channel_regs *) &DISRC3
69 void dma_init(void)
71 /* TODO */
73 /* Enable interupt on DMA Finish */
74 /* Clear pending source */
75 SRCPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
76 INTPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
78 /* Enable interrupt in controller */
79 s3c_regclr32(&INTMOD, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
80 s3c_regclr32(&INTMSK, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
83 void dma_retain(void)
85 /* TODO */
86 dma_used++;
87 if(dma_used > 0)
89 /* Enable DMA controller, clock? */
93 void dma_release(void)
95 /* TODO */
96 if (dma_used > 0)
97 dma_used--;
98 if(dma_used == 0)
100 /* Disable DMA */
105 inline void dma_disable_channel(int channel)
107 struct dma_channel_regs *regs = dma_regs [channel];
109 /* disable the specified channel */
111 /* Reset the channel */
112 regs->dmasktrig |= DMASKTRIG_STOP;
114 /* Wait for DMA controller to be ready */
115 while(regs->dmasktrig & DMASKTRIG_ON)
117 while(regs->dstat & DSTAT_STAT_BUSY)
121 void dma_enable_channel(int channel, struct dma_request *request)
123 struct dma_channel_regs *regs = dma_regs [channel];
125 /* TODO - transfer sizes (assumes word) */
127 if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID)
128 panicf ("DMA: invalid channel");
130 /* setup a transfer on specified channel */
131 dma_disable_channel (channel);
133 if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR)
134 regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR;
135 else
136 regs->disrc = (unsigned long)request->source_addr;
137 regs->disrcc = request->source_control;
139 if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR)
140 regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR;
141 else
142 regs->didst = (unsigned long)request->dest_addr;
143 regs->didstc = request->dest_control;
145 regs->dcon = request->control | request->count |
146 DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL;
148 dma_state [channel].callback = request->callback;
150 /* Activate the channel */
151 invalidate_dcache_range((void *)request->dest_addr, request->count * 4);
153 dma_state [channel].status |= STATUS_CHANNEL_ACTIVE;
154 regs->dmasktrig = DMASKTRIG_ON;
156 if ((request->control & DCON_HW_SEL) == 0)
158 /* Start DMA */
159 regs->dmasktrig |= DMASKTRIG_SW_TRIG;
164 /* ISRs */
165 inline void generic_isr (unsigned channel)
167 if (dma_state [channel].status | STATUS_CHANNEL_ACTIVE)
169 if (dma_state [channel].callback)
170 /* call callback for relevant channel */
171 dma_state [channel].callback();
173 dma_state [channel].status &= ~STATUS_CHANNEL_ACTIVE;
177 void DMA0(void)
179 generic_isr (0);
180 /* Ack the interrupt */
181 SRCPND = DMA0_MASK;
182 INTPND = DMA0_MASK;
185 void DMA1(void)
187 generic_isr (1);
188 /* Ack the interrupt */
189 SRCPND = DMA1_MASK;
190 INTPND = DMA1_MASK;
193 void DMA2(void)
195 generic_isr (2);
196 /* Ack the interrupt */
197 SRCPND = DMA2_MASK;
198 INTPND = DMA2_MASK;
201 void DMA3(void)
203 generic_isr (3);
204 /* Ack the interrupt */
205 SRCPND = DMA3_MASK;
206 INTPND = DMA3_MASK;