2 * On-chip DMA controller framework.
4 * Copyright (C) 2008 Nokia Corporation
5 * Written by Andrzej Zaborowski <andrew@openedhand.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 or
10 * (at your option) version 3 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "qemu-common.h"
22 #include "qemu-timer.h"
25 static void transfer_mem2mem(struct soc_dma_ch_s
*ch
)
27 memcpy(ch
->paddr
[0], ch
->paddr
[1], ch
->bytes
);
28 ch
->paddr
[0] += ch
->bytes
;
29 ch
->paddr
[1] += ch
->bytes
;
32 static void transfer_mem2fifo(struct soc_dma_ch_s
*ch
)
34 ch
->io_fn
[1](ch
->io_opaque
[1], ch
->paddr
[0], ch
->bytes
);
35 ch
->paddr
[0] += ch
->bytes
;
38 static void transfer_fifo2mem(struct soc_dma_ch_s
*ch
)
40 ch
->io_fn
[0](ch
->io_opaque
[0], ch
->paddr
[1], ch
->bytes
);
41 ch
->paddr
[1] += ch
->bytes
;
44 /* This is further optimisable but isn't very important because often
45 * DMA peripherals forbid this kind of transfers and even when they don't,
46 * oprating systems may not need to use them. */
47 static void *fifo_buf
;
49 static void transfer_fifo2fifo(struct soc_dma_ch_s
*ch
)
51 if (ch
->bytes
> fifo_size
)
52 fifo_buf
= qemu_realloc(fifo_buf
, fifo_size
= ch
->bytes
);
54 /* Implement as transfer_fifo2linear + transfer_linear2fifo. */
55 ch
->io_fn
[0](ch
->io_opaque
[0], fifo_buf
, ch
->bytes
);
56 ch
->io_fn
[1](ch
->io_opaque
[1], fifo_buf
, ch
->bytes
);
62 uint64_t ch_enable_mask
;
66 struct memmap_entry_s
{
67 enum soc_dma_port_type type
;
68 target_phys_addr_t addr
;
83 struct soc_dma_ch_s ch
[0];
86 static void soc_dma_ch_schedule(struct soc_dma_ch_s
*ch
, int delay_bytes
)
88 int64_t now
= qemu_get_clock(vm_clock
);
89 struct dma_s
*dma
= (struct dma_s
*) ch
->dma
;
91 qemu_mod_timer(ch
->timer
, now
+ delay_bytes
/ dma
->channel_freq
);
94 static void soc_dma_ch_run(void *opaque
)
96 struct soc_dma_ch_s
*ch
= (struct soc_dma_ch_s
*) opaque
;
99 ch
->dma
->setup_fn(ch
);
104 soc_dma_ch_schedule(ch
, ch
->bytes
);
108 static inline struct memmap_entry_s
*soc_dma_lookup(struct dma_s
*dma
,
109 target_phys_addr_t addr
)
111 struct memmap_entry_s
*lo
;
115 hi
= dma
->memmap_size
;
119 if (lo
[hi
].addr
<= addr
)
126 static inline enum soc_dma_port_type
soc_dma_ch_update_type(
127 struct soc_dma_ch_s
*ch
, int port
)
129 struct dma_s
*dma
= (struct dma_s
*) ch
->dma
;
130 struct memmap_entry_s
*entry
= soc_dma_lookup(dma
, ch
->vaddr
[port
]);
132 if (entry
->type
== soc_dma_port_fifo
) {
133 while (entry
< dma
->memmap
+ dma
->memmap_size
&&
134 entry
->u
.fifo
.out
!= port
)
136 if (entry
->addr
!= ch
->vaddr
[port
] || entry
->u
.fifo
.out
!= port
)
137 return soc_dma_port_other
;
139 if (ch
->type
[port
] != soc_dma_access_const
)
140 return soc_dma_port_other
;
142 ch
->io_fn
[port
] = entry
->u
.fifo
.fn
;
143 ch
->io_opaque
[port
] = entry
->u
.fifo
.opaque
;
144 return soc_dma_port_fifo
;
145 } else if (entry
->type
== soc_dma_port_mem
) {
146 if (entry
->addr
> ch
->vaddr
[port
] ||
147 entry
->addr
+ entry
->u
.mem
.size
<= ch
->vaddr
[port
])
148 return soc_dma_port_other
;
150 /* TODO: support constant memory address for source port as used for
151 * drawing solid rectangles by PalmOS(R). */
152 if (ch
->type
[port
] != soc_dma_access_const
)
153 return soc_dma_port_other
;
155 ch
->paddr
[port
] = (uint8_t *) entry
->u
.mem
.base
+
156 (ch
->vaddr
[port
] - entry
->addr
);
157 /* TODO: save bytes left to the end of the mapping somewhere so we
158 * can check we're not reading beyond it. */
159 return soc_dma_port_mem
;
161 return soc_dma_port_other
;
164 void soc_dma_ch_update(struct soc_dma_ch_s
*ch
)
166 enum soc_dma_port_type src
, dst
;
168 src
= soc_dma_ch_update_type(ch
, 0);
169 if (src
== soc_dma_port_other
) {
171 ch
->transfer_fn
= ch
->dma
->transfer_fn
;
174 dst
= soc_dma_ch_update_type(ch
, 1);
176 /* TODO: use src and dst as array indices. */
177 if (src
== soc_dma_port_mem
&& dst
== soc_dma_port_mem
)
178 ch
->transfer_fn
= transfer_mem2mem
;
179 else if (src
== soc_dma_port_mem
&& dst
== soc_dma_port_fifo
)
180 ch
->transfer_fn
= transfer_mem2fifo
;
181 else if (src
== soc_dma_port_fifo
&& dst
== soc_dma_port_mem
)
182 ch
->transfer_fn
= transfer_fifo2mem
;
183 else if (src
== soc_dma_port_fifo
&& dst
== soc_dma_port_fifo
)
184 ch
->transfer_fn
= transfer_fifo2fifo
;
186 ch
->transfer_fn
= ch
->dma
->transfer_fn
;
188 ch
->update
= (dst
!= soc_dma_port_other
);
191 static void soc_dma_ch_freq_update(struct dma_s
*s
)
193 if (s
->enabled_count
)
194 /* We completely ignore channel priorities and stuff */
195 s
->channel_freq
= s
->soc
.freq
/ s
->enabled_count
;
197 /* TODO: Signal that we want to disable the functional clock and let
198 * the platform code decide what to do with it, i.e. check that
199 * auto-idle is enabled in the clock controller and if we are stopping
200 * the clock, do the same with any parent clocks that had only one
201 * user keeping them on and auto-idle enabled. */;
204 void soc_dma_set_request(struct soc_dma_ch_s
*ch
, int level
)
206 struct dma_s
*dma
= (struct dma_s
*) ch
->dma
;
208 dma
->enabled_count
+= level
- ch
->enable
;
211 dma
->ch_enable_mask
|= 1 << ch
->num
;
213 dma
->ch_enable_mask
&= ~(1 << ch
->num
);
215 if (level
!= ch
->enable
) {
216 soc_dma_ch_freq_update(dma
);
220 qemu_del_timer(ch
->timer
);
221 else if (!ch
->running
)
224 soc_dma_ch_schedule(ch
, 1);
228 void soc_dma_reset(struct soc_dma_s
*soc
)
230 struct dma_s
*s
= (struct dma_s
*) soc
;
233 s
->ch_enable_mask
= 0;
234 s
->enabled_count
= 0;
235 soc_dma_ch_freq_update(s
);
238 /* TODO: take a functional-clock argument */
239 struct soc_dma_s
*soc_dma_init(int n
)
242 struct dma_s
*s
= qemu_mallocz(sizeof(*s
) + n
* sizeof(*s
->ch
));
246 for (i
= 0; i
< n
; i
++) {
247 s
->ch
[i
].dma
= &s
->soc
;
249 s
->ch
[i
].timer
= qemu_new_timer(vm_clock
, soc_dma_ch_run
, &s
->ch
[i
]);
252 soc_dma_reset(&s
->soc
);
258 void soc_dma_port_add_fifo(struct soc_dma_s
*soc
, target_phys_addr_t virt_base
,
259 soc_dma_io_t fn
, void *opaque
, int out
)
261 struct memmap_entry_s
*entry
;
262 struct dma_s
*dma
= (struct dma_s
*) soc
;
264 dma
->memmap
= qemu_realloc(dma
->memmap
, sizeof(*entry
) *
265 (dma
->memmap_size
+ 1));
266 entry
= soc_dma_lookup(dma
, virt_base
);
268 if (dma
->memmap_size
) {
269 if (entry
->type
== soc_dma_port_mem
) {
270 if (entry
->addr
<= virt_base
&&
271 entry
->addr
+ entry
->u
.mem
.size
> virt_base
) {
272 fprintf(stderr
, "%s: FIFO at " TARGET_FMT_lx
273 " collides with RAM region at " TARGET_FMT_lx
274 "-" TARGET_FMT_lx
"\n", __FUNCTION__
,
275 (target_ulong
) virt_base
,
276 (target_ulong
) entry
->addr
, (target_ulong
)
277 (entry
->addr
+ entry
->u
.mem
.size
));
281 if (entry
->addr
<= virt_base
)
284 while (entry
< dma
->memmap
+ dma
->memmap_size
&&
285 entry
->addr
<= virt_base
) {
286 if (entry
->addr
== virt_base
&& entry
->u
.fifo
.out
== out
) {
287 fprintf(stderr
, "%s: FIFO at " TARGET_FMT_lx
288 " collides FIFO at " TARGET_FMT_lx
"\n",
289 __FUNCTION__
, (target_ulong
) virt_base
,
290 (target_ulong
) entry
->addr
);
297 memmove(entry
+ 1, entry
,
298 (uint8_t *) (dma
->memmap
+ dma
->memmap_size
++) -
303 entry
->addr
= virt_base
;
304 entry
->type
= soc_dma_port_fifo
;
305 entry
->u
.fifo
.fn
= fn
;
306 entry
->u
.fifo
.opaque
= opaque
;
307 entry
->u
.fifo
.out
= out
;
310 void soc_dma_port_add_mem(struct soc_dma_s
*soc
, uint8_t *phys_base
,
311 target_phys_addr_t virt_base
, size_t size
)
313 struct memmap_entry_s
*entry
;
314 struct dma_s
*dma
= (struct dma_s
*) soc
;
316 dma
->memmap
= qemu_realloc(dma
->memmap
, sizeof(*entry
) *
317 (dma
->memmap_size
+ 1));
318 entry
= soc_dma_lookup(dma
, virt_base
);
320 if (dma
->memmap_size
) {
321 if (entry
->type
== soc_dma_port_mem
) {
322 if ((entry
->addr
>= virt_base
&& entry
->addr
< virt_base
+ size
) ||
323 (entry
->addr
<= virt_base
&&
324 entry
->addr
+ entry
->u
.mem
.size
> virt_base
)) {
325 fprintf(stderr
, "%s: RAM at " TARGET_FMT_lx
"-" TARGET_FMT_lx
326 " collides with RAM region at " TARGET_FMT_lx
327 "-" TARGET_FMT_lx
"\n", __FUNCTION__
,
328 (target_ulong
) virt_base
,
329 (target_ulong
) (virt_base
+ size
),
330 (target_ulong
) entry
->addr
, (target_ulong
)
331 (entry
->addr
+ entry
->u
.mem
.size
));
335 if (entry
->addr
<= virt_base
)
338 if (entry
->addr
>= virt_base
&&
339 entry
->addr
< virt_base
+ size
) {
340 fprintf(stderr
, "%s: RAM at " TARGET_FMT_lx
"-" TARGET_FMT_lx
341 " collides with FIFO at " TARGET_FMT_lx
343 (target_ulong
) virt_base
,
344 (target_ulong
) (virt_base
+ size
),
345 (target_ulong
) entry
->addr
);
349 while (entry
< dma
->memmap
+ dma
->memmap_size
&&
350 entry
->addr
<= virt_base
)
354 memmove(entry
+ 1, entry
,
355 (uint8_t *) (dma
->memmap
+ dma
->memmap_size
++) -
360 entry
->addr
= virt_base
;
361 entry
->type
= soc_dma_port_mem
;
362 entry
->u
.mem
.base
= phys_base
;
363 entry
->u
.mem
.size
= size
;
366 /* TODO: port removal for ports like PCMCIA memory */