Commit afbb5194d430adc0f1f3a63ea627bc93e8d17c56
1 parent
51fec3cc
Handle on-chip DMA controllers in one place, convert OMAP DMA to use it.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4920 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
896 additions
and
164 deletions
Makefile.target
@@ -594,7 +594,7 @@ OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | @@ -594,7 +594,7 @@ OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | ||
594 | OBJS+= pflash_cfi01.o gumstix.o | 594 | OBJS+= pflash_cfi01.o gumstix.o |
595 | OBJS+= zaurus.o ide.o serial.o nand.o ecc.o spitz.o tosa.o tc6393xb.o | 595 | OBJS+= zaurus.o ide.o serial.o nand.o ecc.o spitz.o tosa.o tc6393xb.o |
596 | OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o | 596 | OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o |
597 | -OBJS+= omap2.o omap_dss.o | 597 | +OBJS+= omap2.o omap_dss.o soc_dma.o |
598 | OBJS+= palm.o tsc210x.o | 598 | OBJS+= palm.o tsc210x.o |
599 | OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o | 599 | OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o |
600 | OBJS+= tsc2005.o | 600 | OBJS+= tsc2005.o |
hw/omap.h
@@ -417,14 +417,14 @@ enum omap_dma_model { | @@ -417,14 +417,14 @@ enum omap_dma_model { | ||
417 | omap_dma_4, | 417 | omap_dma_4, |
418 | }; | 418 | }; |
419 | 419 | ||
420 | -struct omap_dma_s; | ||
421 | -struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, | 420 | +struct soc_dma_s; |
421 | +struct soc_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, | ||
422 | qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, | 422 | qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, |
423 | enum omap_dma_model model); | 423 | enum omap_dma_model model); |
424 | -struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, | 424 | +struct soc_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, |
425 | struct omap_mpu_state_s *mpu, int fifo, | 425 | struct omap_mpu_state_s *mpu, int fifo, |
426 | int chans, omap_clk iclk, omap_clk fclk); | 426 | int chans, omap_clk iclk, omap_clk fclk); |
427 | -void omap_dma_reset(struct omap_dma_s *s); | 427 | +void omap_dma_reset(struct soc_dma_s *s); |
428 | 428 | ||
429 | struct dma_irq_map { | 429 | struct dma_irq_map { |
430 | int ih; | 430 | int ih; |
@@ -494,7 +494,7 @@ struct omap_dma_lcd_channel_s { | @@ -494,7 +494,7 @@ struct omap_dma_lcd_channel_s { | ||
494 | ram_addr_t phys_framebuffer[2]; | 494 | ram_addr_t phys_framebuffer[2]; |
495 | qemu_irq irq; | 495 | qemu_irq irq; |
496 | struct omap_mpu_state_s *mpu; | 496 | struct omap_mpu_state_s *mpu; |
497 | -} *omap_dma_get_lcdch(struct omap_dma_s *s); | 497 | +} *omap_dma_get_lcdch(struct soc_dma_s *s); |
498 | 498 | ||
499 | /* | 499 | /* |
500 | * DMA request numbers for OMAP1 | 500 | * DMA request numbers for OMAP1 |
@@ -882,7 +882,7 @@ struct omap_mpu_state_s { | @@ -882,7 +882,7 @@ struct omap_mpu_state_s { | ||
882 | /* MPU private TIPB peripherals */ | 882 | /* MPU private TIPB peripherals */ |
883 | struct omap_intr_handler_s *ih[2]; | 883 | struct omap_intr_handler_s *ih[2]; |
884 | 884 | ||
885 | - struct omap_dma_s *dma; | 885 | + struct soc_dma_s *dma; |
886 | 886 | ||
887 | struct omap_mpu_timer_s *timer[3]; | 887 | struct omap_mpu_timer_s *timer[3]; |
888 | struct omap_watchdog_timer_s *wdt; | 888 | struct omap_watchdog_timer_s *wdt; |
hw/omap1.c
@@ -24,6 +24,7 @@ | @@ -24,6 +24,7 @@ | ||
24 | #include "sysemu.h" | 24 | #include "sysemu.h" |
25 | #include "qemu-timer.h" | 25 | #include "qemu-timer.h" |
26 | #include "qemu-char.h" | 26 | #include "qemu-char.h" |
27 | +#include "soc_dma.h" | ||
27 | /* We use pc-style serial ports. */ | 28 | /* We use pc-style serial ports. */ |
28 | #include "pc.h" | 29 | #include "pc.h" |
29 | 30 | ||
@@ -4704,6 +4705,12 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, | @@ -4704,6 +4705,12 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, | ||
4704 | s->port[local ].addr_valid = omap_validate_local_addr; | 4705 | s->port[local ].addr_valid = omap_validate_local_addr; |
4705 | s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; | 4706 | s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; |
4706 | 4707 | ||
4708 | + /* Register SDRAM and SRAM DMA ports for fast transfers. */ | ||
4709 | + soc_dma_port_add_mem_ram(s->dma, | ||
4710 | + emiff_base, OMAP_EMIFF_BASE, s->sdram_size); | ||
4711 | + soc_dma_port_add_mem_ram(s->dma, | ||
4712 | + imif_base, OMAP_IMIF_BASE, s->sram_size); | ||
4713 | + | ||
4707 | s->timer[0] = omap_mpu_timer_init(0xfffec500, | 4714 | s->timer[0] = omap_mpu_timer_init(0xfffec500, |
4708 | s->irq[0][OMAP_INT_TIMER1], | 4715 | s->irq[0][OMAP_INT_TIMER1], |
4709 | omap_findclk(s, "mputim_ck")); | 4716 | omap_findclk(s, "mputim_ck")); |
hw/omap2.c
@@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
26 | #include "qemu-timer.h" | 26 | #include "qemu-timer.h" |
27 | #include "qemu-char.h" | 27 | #include "qemu-char.h" |
28 | #include "flash.h" | 28 | #include "flash.h" |
29 | +#include "soc_dma.h" | ||
29 | #include "audio/audio.h" | 30 | #include "audio/audio.h" |
30 | 31 | ||
31 | /* GP timers */ | 32 | /* GP timers */ |
@@ -4493,6 +4494,10 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | @@ -4493,6 +4494,10 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | ||
4493 | omap_findclk(s, "sdma_fclk")); | 4494 | omap_findclk(s, "sdma_fclk")); |
4494 | s->port->addr_valid = omap2_validate_addr; | 4495 | s->port->addr_valid = omap2_validate_addr; |
4495 | 4496 | ||
4497 | + /* Register SDRAM and SRAM ports for fast DMA transfers. */ | ||
4498 | + soc_dma_port_add_mem_ram(s->dma, q2_base, OMAP2_Q2_BASE, s->sdram_size); | ||
4499 | + soc_dma_port_add_mem_ram(s->dma, sram_base, OMAP2_SRAM_BASE, s->sram_size); | ||
4500 | + | ||
4496 | s->uart[0] = omap2_uart_init(omap_l4ta(s->l4, 19), | 4501 | s->uart[0] = omap2_uart_init(omap_l4ta(s->l4, 19), |
4497 | s->irq[0][OMAP_INT_24XX_UART1_IRQ], | 4502 | s->irq[0][OMAP_INT_24XX_UART1_IRQ], |
4498 | omap_findclk(s, "uart1_fclk"), | 4503 | omap_findclk(s, "uart1_fclk"), |
hw/omap_dma.c
@@ -23,6 +23,7 @@ | @@ -23,6 +23,7 @@ | ||
23 | #include "qemu-timer.h" | 23 | #include "qemu-timer.h" |
24 | #include "omap.h" | 24 | #include "omap.h" |
25 | #include "irq.h" | 25 | #include "irq.h" |
26 | +#include "soc_dma.h" | ||
26 | 27 | ||
27 | struct omap_dma_channel_s { | 28 | struct omap_dma_channel_s { |
28 | /* transfer data */ | 29 | /* transfer data */ |
@@ -66,6 +67,7 @@ struct omap_dma_channel_s { | @@ -66,6 +67,7 @@ struct omap_dma_channel_s { | ||
66 | int pending_request; | 67 | int pending_request; |
67 | int waiting_end_prog; | 68 | int waiting_end_prog; |
68 | uint16_t cpc; | 69 | uint16_t cpc; |
70 | + int set_update; | ||
69 | 71 | ||
70 | /* sync type */ | 72 | /* sync type */ |
71 | int fs; | 73 | int fs; |
@@ -89,6 +91,8 @@ struct omap_dma_channel_s { | @@ -89,6 +91,8 @@ struct omap_dma_channel_s { | ||
89 | int pck_elements; | 91 | int pck_elements; |
90 | } active_set; | 92 | } active_set; |
91 | 93 | ||
94 | + struct soc_dma_ch_s *dma; | ||
95 | + | ||
92 | /* unused parameters */ | 96 | /* unused parameters */ |
93 | int write_mode; | 97 | int write_mode; |
94 | int priority; | 98 | int priority; |
@@ -99,12 +103,11 @@ struct omap_dma_channel_s { | @@ -99,12 +103,11 @@ struct omap_dma_channel_s { | ||
99 | }; | 103 | }; |
100 | 104 | ||
101 | struct omap_dma_s { | 105 | struct omap_dma_s { |
102 | - QEMUTimer *tm; | 106 | + struct soc_dma_s *dma; |
107 | + | ||
103 | struct omap_mpu_state_s *mpu; | 108 | struct omap_mpu_state_s *mpu; |
104 | target_phys_addr_t base; | 109 | target_phys_addr_t base; |
105 | omap_clk clk; | 110 | omap_clk clk; |
106 | - int64_t delay; | ||
107 | - uint64_t drq; | ||
108 | qemu_irq irq[4]; | 111 | qemu_irq irq[4]; |
109 | void (*intr_update)(struct omap_dma_s *s); | 112 | void (*intr_update)(struct omap_dma_s *s); |
110 | enum omap_dma_model model; | 113 | enum omap_dma_model model; |
@@ -115,7 +118,6 @@ struct omap_dma_s { | @@ -115,7 +118,6 @@ struct omap_dma_s { | ||
115 | uint32_t caps[5]; | 118 | uint32_t caps[5]; |
116 | uint32_t irqen[4]; | 119 | uint32_t irqen[4]; |
117 | uint32_t irqstat[4]; | 120 | uint32_t irqstat[4]; |
118 | - int run_count; | ||
119 | 121 | ||
120 | int chans; | 122 | int chans; |
121 | struct omap_dma_channel_s ch[32]; | 123 | struct omap_dma_channel_s ch[32]; |
@@ -139,11 +141,10 @@ static inline void omap_dma_interrupts_update(struct omap_dma_s *s) | @@ -139,11 +141,10 @@ static inline void omap_dma_interrupts_update(struct omap_dma_s *s) | ||
139 | return s->intr_update(s); | 141 | return s->intr_update(s); |
140 | } | 142 | } |
141 | 143 | ||
142 | -static void omap_dma_channel_load(struct omap_dma_s *s, | ||
143 | - struct omap_dma_channel_s *ch) | 144 | +static void omap_dma_channel_load(struct omap_dma_channel_s *ch) |
144 | { | 145 | { |
145 | struct omap_dma_reg_set_s *a = &ch->active_set; | 146 | struct omap_dma_reg_set_s *a = &ch->active_set; |
146 | - int i; | 147 | + int i, normal; |
147 | int omap_3_1 = !ch->omap_3_1_compatible_disable; | 148 | int omap_3_1 = !ch->omap_3_1_compatible_disable; |
148 | 149 | ||
149 | /* | 150 | /* |
@@ -189,20 +190,50 @@ static void omap_dma_channel_load(struct omap_dma_s *s, | @@ -189,20 +190,50 @@ static void omap_dma_channel_load(struct omap_dma_s *s, | ||
189 | default: | 190 | default: |
190 | break; | 191 | break; |
191 | } | 192 | } |
193 | + | ||
194 | + normal = !ch->transparent_copy && !ch->constant_fill && | ||
195 | + /* FIFO is big-endian so either (ch->endian[n] == 1) OR | ||
196 | + * (ch->endian_lock[n] == 1) mean no endianism conversion. */ | ||
197 | + (ch->endian[0] | ch->endian_lock[0]) == | ||
198 | + (ch->endian[1] | ch->endian_lock[1]); | ||
199 | + for (i = 0; i < 2; i ++) { | ||
200 | + /* TODO: for a->frame_delta[i] > 0 still use the fast path, just | ||
201 | + * limit min_elems in omap_dma_transfer_setup to the nearest frame | ||
202 | + * end. */ | ||
203 | + if (!a->elem_delta[i] && normal && | ||
204 | + (a->frames == 1 || !a->frame_delta[i])) | ||
205 | + ch->dma->type[i] = soc_dma_access_const; | ||
206 | + else if (a->elem_delta[i] == ch->data_type && normal && | ||
207 | + (a->frames == 1 || !a->frame_delta[i])) | ||
208 | + ch->dma->type[i] = soc_dma_access_linear; | ||
209 | + else | ||
210 | + ch->dma->type[i] = soc_dma_access_other; | ||
211 | + | ||
212 | + ch->dma->vaddr[i] = ch->addr[i]; | ||
213 | + } | ||
214 | + soc_dma_ch_update(ch->dma); | ||
192 | } | 215 | } |
193 | 216 | ||
194 | static void omap_dma_activate_channel(struct omap_dma_s *s, | 217 | static void omap_dma_activate_channel(struct omap_dma_s *s, |
195 | struct omap_dma_channel_s *ch) | 218 | struct omap_dma_channel_s *ch) |
196 | { | 219 | { |
197 | if (!ch->active) { | 220 | if (!ch->active) { |
221 | + if (ch->set_update) { | ||
222 | + /* It's not clear when the active set is supposed to be | ||
223 | + * loaded from registers. We're already loading it when the | ||
224 | + * channel is enabled, and for some guests this is not enough | ||
225 | + * but that may be also because of a race condition (no | ||
226 | + * delays in qemu) in the guest code, which we're just | ||
227 | + * working around here. */ | ||
228 | + omap_dma_channel_load(ch); | ||
229 | + ch->set_update = 0; | ||
230 | + } | ||
231 | + | ||
198 | ch->active = 1; | 232 | ch->active = 1; |
233 | + soc_dma_set_request(ch->dma, 1); | ||
199 | if (ch->sync) | 234 | if (ch->sync) |
200 | ch->status |= SYNC; | 235 | ch->status |= SYNC; |
201 | - s->run_count ++; | ||
202 | } | 236 | } |
203 | - | ||
204 | - if (s->delay && !qemu_timer_pending(s->tm)) | ||
205 | - qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); | ||
206 | } | 237 | } |
207 | 238 | ||
208 | static void omap_dma_deactivate_channel(struct omap_dma_s *s, | 239 | static void omap_dma_deactivate_channel(struct omap_dma_s *s, |
@@ -219,17 +250,14 @@ static void omap_dma_deactivate_channel(struct omap_dma_s *s, | @@ -219,17 +250,14 @@ static void omap_dma_deactivate_channel(struct omap_dma_s *s, | ||
219 | 250 | ||
220 | /* Don't deactive the channel if it is synchronized and the DMA request is | 251 | /* Don't deactive the channel if it is synchronized and the DMA request is |
221 | active */ | 252 | active */ |
222 | - if (ch->sync && ch->enable && (s->drq & (1 << ch->sync))) | 253 | + if (ch->sync && ch->enable && (s->dma->drqbmp & (1 << ch->sync))) |
223 | return; | 254 | return; |
224 | 255 | ||
225 | if (ch->active) { | 256 | if (ch->active) { |
226 | ch->active = 0; | 257 | ch->active = 0; |
227 | ch->status &= ~SYNC; | 258 | ch->status &= ~SYNC; |
228 | - s->run_count --; | 259 | + soc_dma_set_request(ch->dma, 0); |
229 | } | 260 | } |
230 | - | ||
231 | - if (!s->run_count) | ||
232 | - qemu_del_timer(s->tm); | ||
233 | } | 261 | } |
234 | 262 | ||
235 | static void omap_dma_enable_channel(struct omap_dma_s *s, | 263 | static void omap_dma_enable_channel(struct omap_dma_s *s, |
@@ -238,11 +266,11 @@ static void omap_dma_enable_channel(struct omap_dma_s *s, | @@ -238,11 +266,11 @@ static void omap_dma_enable_channel(struct omap_dma_s *s, | ||
238 | if (!ch->enable) { | 266 | if (!ch->enable) { |
239 | ch->enable = 1; | 267 | ch->enable = 1; |
240 | ch->waiting_end_prog = 0; | 268 | ch->waiting_end_prog = 0; |
241 | - omap_dma_channel_load(s, ch); | 269 | + omap_dma_channel_load(ch); |
242 | /* TODO: theoretically if ch->sync && ch->prefetch && | 270 | /* TODO: theoretically if ch->sync && ch->prefetch && |
243 | - * !s->drq[ch->sync], we should also activate and fetch from source | ||
244 | - * and then stall until signalled. */ | ||
245 | - if ((!ch->sync) || (s->drq & (1 << ch->sync))) | 271 | + * !s->dma->drqbmp[ch->sync], we should also activate and fetch |
272 | + * from source and then stall until signalled. */ | ||
273 | + if ((!ch->sync) || (s->dma->drqbmp & (1 << ch->sync))) | ||
246 | omap_dma_activate_channel(s, ch); | 274 | omap_dma_activate_channel(s, ch); |
247 | } | 275 | } |
248 | } | 276 | } |
@@ -338,140 +366,319 @@ static void omap_dma_process_request(struct omap_dma_s *s, int request) | @@ -338,140 +366,319 @@ static void omap_dma_process_request(struct omap_dma_s *s, int request) | ||
338 | omap_dma_interrupts_update(s); | 366 | omap_dma_interrupts_update(s); |
339 | } | 367 | } |
340 | 368 | ||
341 | -static void omap_dma_channel_run(struct omap_dma_s *s) | 369 | +static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma) |
342 | { | 370 | { |
343 | - int n = s->chans; | ||
344 | - uint16_t status; | ||
345 | uint8_t value[4]; | 371 | uint8_t value[4]; |
346 | - struct omap_dma_port_if_s *src_p, *dest_p; | ||
347 | - struct omap_dma_reg_set_s *a; | ||
348 | - struct omap_dma_channel_s *ch; | ||
349 | - | ||
350 | - for (ch = s->ch; n; n --, ch ++) { | ||
351 | - if (!ch->active) | ||
352 | - continue; | ||
353 | - | ||
354 | - a = &ch->active_set; | 372 | + struct omap_dma_channel_s *ch = dma->opaque; |
373 | + struct omap_dma_reg_set_s *a = &ch->active_set; | ||
374 | + int bytes = dma->bytes; | ||
375 | +#ifdef MULTI_REQ | ||
376 | + uint16_t status = ch->status; | ||
377 | +#endif | ||
355 | 378 | ||
356 | - src_p = &s->mpu->port[ch->port[0]]; | ||
357 | - dest_p = &s->mpu->port[ch->port[1]]; | ||
358 | - if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) || | ||
359 | - (!dest_p->addr_valid(s->mpu, a->dest))) { | ||
360 | -#if 0 | ||
361 | - /* Bus time-out */ | ||
362 | - if (ch->interrupts & TIMEOUT_INTR) | ||
363 | - ch->status |= TIMEOUT_INTR; | 379 | + do { |
380 | + /* Transfer a single element */ | ||
381 | + /* FIXME: check the endianness */ | ||
382 | + if (!ch->constant_fill) | ||
383 | + cpu_physical_memory_read(a->src, value, ch->data_type); | ||
384 | + else | ||
385 | + *(uint32_t *) value = ch->color; | ||
386 | + | ||
387 | + if (!ch->transparent_copy || *(uint32_t *) value != ch->color) | ||
388 | + cpu_physical_memory_write(a->dest, value, ch->data_type); | ||
389 | + | ||
390 | + a->src += a->elem_delta[0]; | ||
391 | + a->dest += a->elem_delta[1]; | ||
392 | + a->element ++; | ||
393 | + | ||
394 | +#ifndef MULTI_REQ | ||
395 | + if (a->element == a->elements) { | ||
396 | + /* End of Frame */ | ||
397 | + a->element = 0; | ||
398 | + a->src += a->frame_delta[0]; | ||
399 | + a->dest += a->frame_delta[1]; | ||
400 | + a->frame ++; | ||
401 | + | ||
402 | + /* If the channel is async, update cpc */ | ||
403 | + if (!ch->sync) | ||
404 | + ch->cpc = a->dest & 0xffff; | ||
405 | + } | ||
406 | + } while ((bytes -= ch->data_type)); | ||
407 | +#else | ||
408 | + /* If the channel is element synchronized, deactivate it */ | ||
409 | + if (ch->sync && !ch->fs && !ch->bs) | ||
364 | omap_dma_deactivate_channel(s, ch); | 410 | omap_dma_deactivate_channel(s, ch); |
365 | - continue; | ||
366 | -#endif | ||
367 | - printf("%s: Bus time-out in DMA%i operation\n", | ||
368 | - __FUNCTION__, s->chans - n); | 411 | + |
412 | + /* If it is the last frame, set the LAST_FRAME interrupt */ | ||
413 | + if (a->element == 1 && a->frame == a->frames - 1) | ||
414 | + if (ch->interrupts & LAST_FRAME_INTR) | ||
415 | + ch->status |= LAST_FRAME_INTR; | ||
416 | + | ||
417 | + /* If the half of the frame was reached, set the HALF_FRAME | ||
418 | + interrupt */ | ||
419 | + if (a->element == (a->elements >> 1)) | ||
420 | + if (ch->interrupts & HALF_FRAME_INTR) | ||
421 | + ch->status |= HALF_FRAME_INTR; | ||
422 | + | ||
423 | + if (ch->fs && ch->bs) { | ||
424 | + a->pck_element ++; | ||
425 | + /* Check if a full packet has beed transferred. */ | ||
426 | + if (a->pck_element == a->pck_elements) { | ||
427 | + a->pck_element = 0; | ||
428 | + | ||
429 | + /* Set the END_PKT interrupt */ | ||
430 | + if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync) | ||
431 | + ch->status |= END_PKT_INTR; | ||
432 | + | ||
433 | + /* If the channel is packet-synchronized, deactivate it */ | ||
434 | + if (ch->sync) | ||
435 | + omap_dma_deactivate_channel(s, ch); | ||
436 | + } | ||
369 | } | 437 | } |
370 | 438 | ||
371 | - status = ch->status; | ||
372 | - while (status == ch->status && ch->active) { | ||
373 | - /* Transfer a single element */ | ||
374 | - /* FIXME: check the endianness */ | ||
375 | - if (!ch->constant_fill) | ||
376 | - cpu_physical_memory_read(a->src, value, ch->data_type); | ||
377 | - else | ||
378 | - *(uint32_t *) value = ch->color; | ||
379 | - | ||
380 | - if (!ch->transparent_copy || | ||
381 | - *(uint32_t *) value != ch->color) | ||
382 | - cpu_physical_memory_write(a->dest, value, ch->data_type); | ||
383 | - | ||
384 | - a->src += a->elem_delta[0]; | ||
385 | - a->dest += a->elem_delta[1]; | ||
386 | - a->element ++; | ||
387 | - | ||
388 | - /* If the channel is element synchronized, deactivate it */ | ||
389 | - if (ch->sync && !ch->fs && !ch->bs) | 439 | + if (a->element == a->elements) { |
440 | + /* End of Frame */ | ||
441 | + a->element = 0; | ||
442 | + a->src += a->frame_delta[0]; | ||
443 | + a->dest += a->frame_delta[1]; | ||
444 | + a->frame ++; | ||
445 | + | ||
446 | + /* If the channel is frame synchronized, deactivate it */ | ||
447 | + if (ch->sync && ch->fs && !ch->bs) | ||
390 | omap_dma_deactivate_channel(s, ch); | 448 | omap_dma_deactivate_channel(s, ch); |
391 | 449 | ||
392 | - /* If it is the last frame, set the LAST_FRAME interrupt */ | ||
393 | - if (a->element == 1 && a->frame == a->frames - 1) | ||
394 | - if (ch->interrupts & LAST_FRAME_INTR) | ||
395 | - ch->status |= LAST_FRAME_INTR; | ||
396 | - | ||
397 | - /* If the half of the frame was reached, set the HALF_FRAME | ||
398 | - interrupt */ | ||
399 | - if (a->element == (a->elements >> 1)) | ||
400 | - if (ch->interrupts & HALF_FRAME_INTR) | ||
401 | - ch->status |= HALF_FRAME_INTR; | ||
402 | - | ||
403 | - if (ch->fs && ch->bs) { | ||
404 | - a->pck_element ++; | ||
405 | - /* Check if a full packet has beed transferred. */ | ||
406 | - if (a->pck_element == a->pck_elements) { | ||
407 | - a->pck_element = 0; | ||
408 | - | ||
409 | - /* Set the END_PKT interrupt */ | ||
410 | - if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync) | ||
411 | - ch->status |= END_PKT_INTR; | ||
412 | - | ||
413 | - /* If the channel is packet-synchronized, deactivate it */ | ||
414 | - if (ch->sync) | 450 | + /* If the channel is async, update cpc */ |
451 | + if (!ch->sync) | ||
452 | + ch->cpc = a->dest & 0xffff; | ||
453 | + | ||
454 | + /* Set the END_FRAME interrupt */ | ||
455 | + if (ch->interrupts & END_FRAME_INTR) | ||
456 | + ch->status |= END_FRAME_INTR; | ||
457 | + | ||
458 | + if (a->frame == a->frames) { | ||
459 | + /* End of Block */ | ||
460 | + /* Disable the channel */ | ||
461 | + | ||
462 | + if (ch->omap_3_1_compatible_disable) { | ||
463 | + omap_dma_disable_channel(s, ch); | ||
464 | + if (ch->link_enabled) | ||
465 | + omap_dma_enable_channel(s, | ||
466 | + &s->ch[ch->link_next_ch]); | ||
467 | + } else { | ||
468 | + if (!ch->auto_init) | ||
469 | + omap_dma_disable_channel(s, ch); | ||
470 | + else if (ch->repeat || ch->end_prog) | ||
471 | + omap_dma_channel_load(ch); | ||
472 | + else { | ||
473 | + ch->waiting_end_prog = 1; | ||
415 | omap_dma_deactivate_channel(s, ch); | 474 | omap_dma_deactivate_channel(s, ch); |
475 | + } | ||
416 | } | 476 | } |
477 | + | ||
478 | + if (ch->interrupts & END_BLOCK_INTR) | ||
479 | + ch->status |= END_BLOCK_INTR; | ||
417 | } | 480 | } |
481 | + } | ||
482 | + } while (status == ch->status && ch->active); | ||
418 | 483 | ||
419 | - if (a->element == a->elements) { | ||
420 | - /* End of Frame */ | ||
421 | - a->element = 0; | ||
422 | - a->src += a->frame_delta[0]; | ||
423 | - a->dest += a->frame_delta[1]; | ||
424 | - a->frame ++; | 484 | + omap_dma_interrupts_update(s); |
485 | +#endif | ||
486 | +} | ||
425 | 487 | ||
426 | - /* If the channel is frame synchronized, deactivate it */ | ||
427 | - if (ch->sync && ch->fs && !ch->bs) | ||
428 | - omap_dma_deactivate_channel(s, ch); | 488 | +enum { |
489 | + omap_dma_intr_element_sync, | ||
490 | + omap_dma_intr_last_frame, | ||
491 | + omap_dma_intr_half_frame, | ||
492 | + omap_dma_intr_frame, | ||
493 | + omap_dma_intr_frame_sync, | ||
494 | + omap_dma_intr_packet, | ||
495 | + omap_dma_intr_packet_sync, | ||
496 | + omap_dma_intr_block, | ||
497 | + __omap_dma_intr_last, | ||
498 | +}; | ||
429 | 499 | ||
430 | - /* If the channel is async, update cpc */ | ||
431 | - if (!ch->sync) | ||
432 | - ch->cpc = a->dest & 0xffff; | 500 | +static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) |
501 | +{ | ||
502 | + struct omap_dma_port_if_s *src_p, *dest_p; | ||
503 | + struct omap_dma_reg_set_s *a; | ||
504 | + struct omap_dma_channel_s *ch = dma->opaque; | ||
505 | + struct omap_dma_s *s = dma->dma->opaque; | ||
506 | + int frames, min_elems, elements[__omap_dma_intr_last]; | ||
433 | 507 | ||
434 | - /* Set the END_FRAME interrupt */ | ||
435 | - if (ch->interrupts & END_FRAME_INTR) | ||
436 | - ch->status |= END_FRAME_INTR; | 508 | + a = &ch->active_set; |
437 | 509 | ||
438 | - if (a->frame == a->frames) { | ||
439 | - /* End of Block */ | ||
440 | - /* Disable the channel */ | 510 | + src_p = &s->mpu->port[ch->port[0]]; |
511 | + dest_p = &s->mpu->port[ch->port[1]]; | ||
512 | + if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) || | ||
513 | + (!dest_p->addr_valid(s->mpu, a->dest))) { | ||
514 | +#if 0 | ||
515 | + /* Bus time-out */ | ||
516 | + if (ch->interrupts & TIMEOUT_INTR) | ||
517 | + ch->status |= TIMEOUT_INTR; | ||
518 | + omap_dma_deactivate_channel(s, ch); | ||
519 | + continue; | ||
520 | +#endif | ||
521 | + printf("%s: Bus time-out in DMA%i operation\n", | ||
522 | + __FUNCTION__, dma->num); | ||
523 | + } | ||
441 | 524 | ||
442 | - if (ch->omap_3_1_compatible_disable) { | ||
443 | - omap_dma_disable_channel(s, ch); | ||
444 | - if (ch->link_enabled) | ||
445 | - omap_dma_enable_channel(s, | ||
446 | - &s->ch[ch->link_next_ch]); | ||
447 | - } else { | ||
448 | - if (!ch->auto_init) | ||
449 | - omap_dma_disable_channel(s, ch); | ||
450 | - else if (ch->repeat || ch->end_prog) | ||
451 | - omap_dma_channel_load(s, ch); | ||
452 | - else { | ||
453 | - ch->waiting_end_prog = 1; | ||
454 | - omap_dma_deactivate_channel(s, ch); | ||
455 | - } | ||
456 | - } | 525 | + min_elems = INT_MAX; |
526 | + | ||
527 | + /* Check all the conditions that terminate the transfer starting | ||
528 | + * with those that can occur the soonest. */ | ||
529 | +#define INTR_CHECK(cond, id, nelements) \ | ||
530 | + if (cond) { \ | ||
531 | + elements[id] = nelements; \ | ||
532 | + if (elements[id] < min_elems) \ | ||
533 | + min_elems = elements[id]; \ | ||
534 | + } else \ | ||
535 | + elements[id] = INT_MAX; | ||
536 | + | ||
537 | + /* Elements */ | ||
538 | + INTR_CHECK( | ||
539 | + ch->sync && !ch->fs && !ch->bs, | ||
540 | + omap_dma_intr_element_sync, | ||
541 | + 1) | ||
542 | + | ||
543 | + /* Frames */ | ||
544 | + /* TODO: for transfers where entire frames can be read and written | ||
545 | + * using memcpy() but a->frame_delta is non-zero, try to still do | ||
546 | + * transfers using soc_dma but limit min_elems to a->elements - ... | ||
547 | + * See also the TODO in omap_dma_channel_load. */ | ||
548 | + INTR_CHECK( | ||
549 | + (ch->interrupts & LAST_FRAME_INTR) && | ||
550 | + ((a->frame < a->frames - 1) || !a->element), | ||
551 | + omap_dma_intr_last_frame, | ||
552 | + (a->frames - a->frame - 2) * a->elements + | ||
553 | + (a->elements - a->element + 1)) | ||
554 | + INTR_CHECK( | ||
555 | + ch->interrupts & HALF_FRAME_INTR, | ||
556 | + omap_dma_intr_half_frame, | ||
557 | + (a->elements >> 1) + | ||
558 | + (a->element >= (a->elements >> 1) ? a->elements : 0) - | ||
559 | + a->element) | ||
560 | + INTR_CHECK( | ||
561 | + ch->sync && ch->fs && (ch->interrupts & END_FRAME_INTR), | ||
562 | + omap_dma_intr_frame, | ||
563 | + a->elements - a->element) | ||
564 | + INTR_CHECK( | ||
565 | + ch->sync && ch->fs && !ch->bs, | ||
566 | + omap_dma_intr_frame_sync, | ||
567 | + a->elements - a->element) | ||
568 | + | ||
569 | + /* Packets */ | ||
570 | + INTR_CHECK( | ||
571 | + ch->fs && ch->bs && | ||
572 | + (ch->interrupts & END_PKT_INTR) && !ch->src_sync, | ||
573 | + omap_dma_intr_packet, | ||
574 | + a->pck_elements - a->pck_element) | ||
575 | + INTR_CHECK( | ||
576 | + ch->fs && ch->bs && ch->sync, | ||
577 | + omap_dma_intr_packet_sync, | ||
578 | + a->pck_elements - a->pck_element) | ||
579 | + | ||
580 | + /* Blocks */ | ||
581 | + INTR_CHECK( | ||
582 | + 1, | ||
583 | + omap_dma_intr_block, | ||
584 | + (a->frames - a->frame - 1) * a->elements + | ||
585 | + (a->elements - a->element)) | ||
586 | + | ||
587 | + dma->bytes = min_elems * ch->data_type; | ||
588 | + | ||
589 | + /* Set appropriate interrupts and/or deactivate channels */ | ||
590 | + | ||
591 | +#ifdef MULTI_REQ | ||
592 | + /* TODO: should all of this only be done if dma->update, and otherwise | ||
593 | + * inside omap_dma_transfer_generic below - check what's faster. */ | ||
594 | + if (dma->update) { | ||
595 | +#endif | ||
457 | 596 | ||
458 | - if (ch->interrupts & END_BLOCK_INTR) | ||
459 | - ch->status |= END_BLOCK_INTR; | ||
460 | - } | 597 | + /* If the channel is element synchronized, deactivate it */ |
598 | + if (min_elems == elements[omap_dma_intr_element_sync]) | ||
599 | + omap_dma_deactivate_channel(s, ch); | ||
600 | + | ||
601 | + /* If it is the last frame, set the LAST_FRAME interrupt */ | ||
602 | + if (min_elems == elements[omap_dma_intr_last_frame]) | ||
603 | + ch->status |= LAST_FRAME_INTR; | ||
604 | + | ||
605 | + /* If exactly half of the frame was reached, set the HALF_FRAME | ||
606 | + interrupt */ | ||
607 | + if (min_elems == elements[omap_dma_intr_half_frame]) | ||
608 | + ch->status |= HALF_FRAME_INTR; | ||
609 | + | ||
610 | + /* If a full packet has been transferred, set the END_PKT interrupt */ | ||
611 | + if (min_elems == elements[omap_dma_intr_packet]) | ||
612 | + ch->status |= END_PKT_INTR; | ||
613 | + | ||
614 | + /* If the channel is packet-synchronized, deactivate it */ | ||
615 | + if (min_elems == elements[omap_dma_intr_packet_sync]) | ||
616 | + omap_dma_deactivate_channel(s, ch); | ||
617 | + | ||
618 | + /* If the channel is frame synchronized, deactivate it */ | ||
619 | + if (min_elems == elements[omap_dma_intr_frame_sync]) | ||
620 | + omap_dma_deactivate_channel(s, ch); | ||
621 | + | ||
622 | + /* Set the END_FRAME interrupt */ | ||
623 | + if (min_elems == elements[omap_dma_intr_frame]) | ||
624 | + ch->status |= END_FRAME_INTR; | ||
625 | + | ||
626 | + if (min_elems == elements[omap_dma_intr_block]) { | ||
627 | + /* End of Block */ | ||
628 | + /* Disable the channel */ | ||
629 | + | ||
630 | + if (ch->omap_3_1_compatible_disable) { | ||
631 | + omap_dma_disable_channel(s, ch); | ||
632 | + if (ch->link_enabled) | ||
633 | + omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]); | ||
634 | + } else { | ||
635 | + if (!ch->auto_init) | ||
636 | + omap_dma_disable_channel(s, ch); | ||
637 | + else if (ch->repeat || ch->end_prog) | ||
638 | + omap_dma_channel_load(ch); | ||
639 | + else { | ||
640 | + ch->waiting_end_prog = 1; | ||
641 | + omap_dma_deactivate_channel(s, ch); | ||
461 | } | 642 | } |
462 | } | 643 | } |
644 | + | ||
645 | + if (ch->interrupts & END_BLOCK_INTR) | ||
646 | + ch->status |= END_BLOCK_INTR; | ||
647 | + } | ||
648 | + | ||
649 | + /* Update packet number */ | ||
650 | + if (ch->fs && ch->bs) { | ||
651 | + a->pck_element += min_elems; | ||
652 | + a->pck_element %= a->pck_elements; | ||
653 | + } | ||
654 | + | ||
655 | + /* TODO: check if we really need to update anything here or perhaps we | ||
656 | + * can skip part of this. */ | ||
657 | +#ifndef MULTI_REQ | ||
658 | + if (dma->update) { | ||
659 | +#endif | ||
660 | + a->element += min_elems; | ||
661 | + | ||
662 | + frames = a->element / a->elements; | ||
663 | + a->element = a->element % a->elements; | ||
664 | + a->frame += frames; | ||
665 | + a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; | ||
666 | + a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; | ||
667 | + | ||
668 | + /* If the channel is async, update cpc */ | ||
669 | + if (!ch->sync && frames) | ||
670 | + ch->cpc = a->dest & 0xffff; | ||
463 | } | 671 | } |
464 | 672 | ||
465 | omap_dma_interrupts_update(s); | 673 | omap_dma_interrupts_update(s); |
466 | - if (s->run_count && s->delay) | ||
467 | - qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); | ||
468 | } | 674 | } |
469 | 675 | ||
470 | -void omap_dma_reset(struct omap_dma_s *s) | 676 | +void omap_dma_reset(struct soc_dma_s *dma) |
471 | { | 677 | { |
472 | int i; | 678 | int i; |
679 | + struct omap_dma_s *s = dma->opaque; | ||
473 | 680 | ||
474 | - qemu_del_timer(s->tm); | 681 | + soc_dma_reset(s->dma); |
475 | if (s->model < omap_dma_4) | 682 | if (s->model < omap_dma_4) |
476 | s->gcr = 0x0004; | 683 | s->gcr = 0x0004; |
477 | else | 684 | else |
@@ -479,8 +686,6 @@ void omap_dma_reset(struct omap_dma_s *s) | @@ -479,8 +686,6 @@ void omap_dma_reset(struct omap_dma_s *s) | ||
479 | s->ocp = 0x00000000; | 686 | s->ocp = 0x00000000; |
480 | memset(&s->irqstat, 0, sizeof(s->irqstat)); | 687 | memset(&s->irqstat, 0, sizeof(s->irqstat)); |
481 | memset(&s->irqen, 0, sizeof(s->irqen)); | 688 | memset(&s->irqen, 0, sizeof(s->irqen)); |
482 | - s->drq = 0x00000000; | ||
483 | - s->run_count = 0; | ||
484 | s->lcd_ch.src = emiff; | 689 | s->lcd_ch.src = emiff; |
485 | s->lcd_ch.condition = 0; | 690 | s->lcd_ch.condition = 0; |
486 | s->lcd_ch.interrupts = 0; | 691 | s->lcd_ch.interrupts = 0; |
@@ -1161,7 +1366,7 @@ static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) | @@ -1161,7 +1366,7 @@ static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) | ||
1161 | 1366 | ||
1162 | case 0x408: /* DMA_GRST */ | 1367 | case 0x408: /* DMA_GRST */ |
1163 | if (value & 0x1) | 1368 | if (value & 0x1) |
1164 | - omap_dma_reset(s); | 1369 | + omap_dma_reset(s->dma); |
1165 | break; | 1370 | break; |
1166 | 1371 | ||
1167 | default: | 1372 | default: |
@@ -1338,27 +1543,25 @@ static void omap_dma_request(void *opaque, int drq, int req) | @@ -1338,27 +1543,25 @@ static void omap_dma_request(void *opaque, int drq, int req) | ||
1338 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; | 1543 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; |
1339 | /* The request pins are level triggered in QEMU. */ | 1544 | /* The request pins are level triggered in QEMU. */ |
1340 | if (req) { | 1545 | if (req) { |
1341 | - if (~s->drq & (1 << drq)) { | ||
1342 | - s->drq |= 1 << drq; | 1546 | + if (~s->dma->drqbmp & (1 << drq)) { |
1547 | + s->dma->drqbmp |= 1 << drq; | ||
1343 | omap_dma_process_request(s, drq); | 1548 | omap_dma_process_request(s, drq); |
1344 | } | 1549 | } |
1345 | } else | 1550 | } else |
1346 | - s->drq &= ~(1 << drq); | 1551 | + s->dma->drqbmp &= ~(1 << drq); |
1347 | } | 1552 | } |
1348 | 1553 | ||
1554 | +/* XXX: this won't be needed once soc_dma knows about clocks. */ | ||
1349 | static void omap_dma_clk_update(void *opaque, int line, int on) | 1555 | static void omap_dma_clk_update(void *opaque, int line, int on) |
1350 | { | 1556 | { |
1351 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; | 1557 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; |
1558 | + int i; | ||
1352 | 1559 | ||
1353 | - if (on) { | ||
1354 | - /* TODO: make a clever calculation */ | ||
1355 | - s->delay = ticks_per_sec >> 8; | ||
1356 | - if (s->run_count) | ||
1357 | - qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); | ||
1358 | - } else { | ||
1359 | - s->delay = 0; | ||
1360 | - qemu_del_timer(s->tm); | ||
1361 | - } | 1560 | + s->dma->freq = omap_clk_getrate(s->clk); |
1561 | + | ||
1562 | + for (i = 0; i < s->chans; i ++) | ||
1563 | + if (s->ch[i].active) | ||
1564 | + soc_dma_set_request(s->ch[i].dma, on); | ||
1362 | } | 1565 | } |
1363 | 1566 | ||
1364 | static void omap_dma_setcaps(struct omap_dma_s *s) | 1567 | static void omap_dma_setcaps(struct omap_dma_s *s) |
@@ -1407,7 +1610,7 @@ static void omap_dma_setcaps(struct omap_dma_s *s) | @@ -1407,7 +1610,7 @@ static void omap_dma_setcaps(struct omap_dma_s *s) | ||
1407 | } | 1610 | } |
1408 | } | 1611 | } |
1409 | 1612 | ||
1410 | -struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, | 1613 | +struct soc_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, |
1411 | qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, | 1614 | qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, |
1412 | enum omap_dma_model model) | 1615 | enum omap_dma_model model) |
1413 | { | 1616 | { |
@@ -1428,24 +1631,37 @@ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, | @@ -1428,24 +1631,37 @@ struct omap_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs, | ||
1428 | s->clk = clk; | 1631 | s->clk = clk; |
1429 | s->lcd_ch.irq = lcd_irq; | 1632 | s->lcd_ch.irq = lcd_irq; |
1430 | s->lcd_ch.mpu = mpu; | 1633 | s->lcd_ch.mpu = mpu; |
1431 | - omap_dma_setcaps(s); | 1634 | + |
1635 | + s->dma = soc_dma_init((model <= omap_dma_3_1) ? 9 : 16); | ||
1636 | + s->dma->freq = omap_clk_getrate(clk); | ||
1637 | + s->dma->transfer_fn = omap_dma_transfer_generic; | ||
1638 | + s->dma->setup_fn = omap_dma_transfer_setup; | ||
1639 | + s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 32); | ||
1640 | + s->dma->opaque = s; | ||
1641 | + | ||
1432 | while (num_irqs --) | 1642 | while (num_irqs --) |
1433 | s->ch[num_irqs].irq = irqs[num_irqs]; | 1643 | s->ch[num_irqs].irq = irqs[num_irqs]; |
1434 | for (i = 0; i < 3; i ++) { | 1644 | for (i = 0; i < 3; i ++) { |
1435 | s->ch[i].sibling = &s->ch[i + 6]; | 1645 | s->ch[i].sibling = &s->ch[i + 6]; |
1436 | s->ch[i + 6].sibling = &s->ch[i]; | 1646 | s->ch[i + 6].sibling = &s->ch[i]; |
1437 | } | 1647 | } |
1438 | - s->tm = qemu_new_timer(vm_clock, (QEMUTimerCB *) omap_dma_channel_run, s); | 1648 | + for (i = (model <= omap_dma_3_1) ? 8 : 15; i >= 0; i --) { |
1649 | + s->ch[i].dma = &s->dma->ch[i]; | ||
1650 | + s->dma->ch[i].opaque = &s->ch[i]; | ||
1651 | + } | ||
1652 | + | ||
1653 | + omap_dma_setcaps(s); | ||
1439 | omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); | 1654 | omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); |
1440 | - mpu->drq = qemu_allocate_irqs(omap_dma_request, s, 32); | ||
1441 | - omap_dma_reset(s); | 1655 | + omap_dma_reset(s->dma); |
1442 | omap_dma_clk_update(s, 0, 1); | 1656 | omap_dma_clk_update(s, 0, 1); |
1443 | 1657 | ||
1444 | iomemtype = cpu_register_io_memory(0, omap_dma_readfn, | 1658 | iomemtype = cpu_register_io_memory(0, omap_dma_readfn, |
1445 | omap_dma_writefn, s); | 1659 | omap_dma_writefn, s); |
1446 | cpu_register_physical_memory(s->base, memsize, iomemtype); | 1660 | cpu_register_physical_memory(s->base, memsize, iomemtype); |
1447 | 1661 | ||
1448 | - return s; | 1662 | + mpu->drq = s->dma->drq; |
1663 | + | ||
1664 | + return s->dma; | ||
1449 | } | 1665 | } |
1450 | 1666 | ||
1451 | static void omap_dma_interrupts_4_update(struct omap_dma_s *s) | 1667 | static void omap_dma_interrupts_4_update(struct omap_dma_s *s) |
@@ -1646,7 +1862,7 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | @@ -1646,7 +1862,7 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | ||
1646 | 1862 | ||
1647 | case 0x2c: /* DMA4_OCP_SYSCONFIG */ | 1863 | case 0x2c: /* DMA4_OCP_SYSCONFIG */ |
1648 | if (value & 2) /* SOFTRESET */ | 1864 | if (value & 2) /* SOFTRESET */ |
1649 | - omap_dma_reset(s); | 1865 | + omap_dma_reset(s->dma); |
1650 | s->ocp = value & 0x3321; | 1866 | s->ocp = value & 0x3321; |
1651 | if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ | 1867 | if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ |
1652 | fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__); | 1868 | fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__); |
@@ -1728,7 +1944,7 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | @@ -1728,7 +1944,7 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | ||
1728 | ch->endian[1] =(value >> 19) & 1; | 1944 | ch->endian[1] =(value >> 19) & 1; |
1729 | ch->endian_lock[1] =(value >> 18) & 1; | 1945 | ch->endian_lock[1] =(value >> 18) & 1; |
1730 | if (ch->endian[0] != ch->endian[1]) | 1946 | if (ch->endian[0] != ch->endian[1]) |
1731 | - fprintf(stderr, "%s: DMA endianned conversion enable attempt\n", | 1947 | + fprintf(stderr, "%s: DMA endiannes conversion enable attempt\n", |
1732 | __FUNCTION__); | 1948 | __FUNCTION__); |
1733 | ch->write_mode = (value >> 16) & 3; | 1949 | ch->write_mode = (value >> 16) & 3; |
1734 | ch->burst[1] = (value & 0xc000) >> 14; | 1950 | ch->burst[1] = (value & 0xc000) >> 14; |
@@ -1746,35 +1962,43 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | @@ -1746,35 +1962,43 @@ static void omap_dma4_write(void *opaque, target_phys_addr_t addr, | ||
1746 | break; | 1962 | break; |
1747 | 1963 | ||
1748 | case 0x14: /* DMA4_CEN */ | 1964 | case 0x14: /* DMA4_CEN */ |
1965 | + ch->set_update = 1; | ||
1749 | ch->elements = value & 0xffffff; | 1966 | ch->elements = value & 0xffffff; |
1750 | break; | 1967 | break; |
1751 | 1968 | ||
1752 | case 0x18: /* DMA4_CFN */ | 1969 | case 0x18: /* DMA4_CFN */ |
1753 | ch->frames = value & 0xffff; | 1970 | ch->frames = value & 0xffff; |
1971 | + ch->set_update = 1; | ||
1754 | break; | 1972 | break; |
1755 | 1973 | ||
1756 | case 0x1c: /* DMA4_CSSA */ | 1974 | case 0x1c: /* DMA4_CSSA */ |
1757 | ch->addr[0] = (target_phys_addr_t) (uint32_t) value; | 1975 | ch->addr[0] = (target_phys_addr_t) (uint32_t) value; |
1976 | + ch->set_update = 1; | ||
1758 | break; | 1977 | break; |
1759 | 1978 | ||
1760 | case 0x20: /* DMA4_CDSA */ | 1979 | case 0x20: /* DMA4_CDSA */ |
1761 | ch->addr[1] = (target_phys_addr_t) (uint32_t) value; | 1980 | ch->addr[1] = (target_phys_addr_t) (uint32_t) value; |
1981 | + ch->set_update = 1; | ||
1762 | break; | 1982 | break; |
1763 | 1983 | ||
1764 | case 0x24: /* DMA4_CSEI */ | 1984 | case 0x24: /* DMA4_CSEI */ |
1765 | ch->element_index[0] = (int16_t) value; | 1985 | ch->element_index[0] = (int16_t) value; |
1986 | + ch->set_update = 1; | ||
1766 | break; | 1987 | break; |
1767 | 1988 | ||
1768 | case 0x28: /* DMA4_CSFI */ | 1989 | case 0x28: /* DMA4_CSFI */ |
1769 | ch->frame_index[0] = (int32_t) value; | 1990 | ch->frame_index[0] = (int32_t) value; |
1991 | + ch->set_update = 1; | ||
1770 | break; | 1992 | break; |
1771 | 1993 | ||
1772 | case 0x2c: /* DMA4_CDEI */ | 1994 | case 0x2c: /* DMA4_CDEI */ |
1773 | ch->element_index[1] = (int16_t) value; | 1995 | ch->element_index[1] = (int16_t) value; |
1996 | + ch->set_update = 1; | ||
1774 | break; | 1997 | break; |
1775 | 1998 | ||
1776 | case 0x30: /* DMA4_CDFI */ | 1999 | case 0x30: /* DMA4_CDFI */ |
1777 | ch->frame_index[1] = (int32_t) value; | 2000 | ch->frame_index[1] = (int32_t) value; |
2001 | + ch->set_update = 1; | ||
1778 | break; | 2002 | break; |
1779 | 2003 | ||
1780 | case 0x44: /* DMA4_COLOR */ | 2004 | case 0x44: /* DMA4_COLOR */ |
@@ -1806,11 +2030,11 @@ static CPUWriteMemoryFunc *omap_dma4_writefn[] = { | @@ -1806,11 +2030,11 @@ static CPUWriteMemoryFunc *omap_dma4_writefn[] = { | ||
1806 | omap_dma4_write, | 2030 | omap_dma4_write, |
1807 | }; | 2031 | }; |
1808 | 2032 | ||
1809 | -struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, | 2033 | +struct soc_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, |
1810 | struct omap_mpu_state_s *mpu, int fifo, | 2034 | struct omap_mpu_state_s *mpu, int fifo, |
1811 | int chans, omap_clk iclk, omap_clk fclk) | 2035 | int chans, omap_clk iclk, omap_clk fclk) |
1812 | { | 2036 | { |
1813 | - int iomemtype; | 2037 | + int iomemtype, i; |
1814 | struct omap_dma_s *s = (struct omap_dma_s *) | 2038 | struct omap_dma_s *s = (struct omap_dma_s *) |
1815 | qemu_mallocz(sizeof(struct omap_dma_s)); | 2039 | qemu_mallocz(sizeof(struct omap_dma_s)); |
1816 | 2040 | ||
@@ -1819,23 +2043,38 @@ struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, | @@ -1819,23 +2043,38 @@ struct omap_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs, | ||
1819 | s->chans = chans; | 2043 | s->chans = chans; |
1820 | s->mpu = mpu; | 2044 | s->mpu = mpu; |
1821 | s->clk = fclk; | 2045 | s->clk = fclk; |
2046 | + | ||
2047 | + s->dma = soc_dma_init(s->chans); | ||
2048 | + s->dma->freq = omap_clk_getrate(fclk); | ||
2049 | + s->dma->transfer_fn = omap_dma_transfer_generic; | ||
2050 | + s->dma->setup_fn = omap_dma_transfer_setup; | ||
2051 | + s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 64); | ||
2052 | + s->dma->opaque = s; | ||
2053 | + for (i = 0; i < s->chans; i ++) { | ||
2054 | + s->ch[i].dma = &s->dma->ch[i]; | ||
2055 | + s->dma->ch[i].opaque = &s->ch[i]; | ||
2056 | + } | ||
2057 | + | ||
1822 | memcpy(&s->irq, irqs, sizeof(s->irq)); | 2058 | memcpy(&s->irq, irqs, sizeof(s->irq)); |
1823 | s->intr_update = omap_dma_interrupts_4_update; | 2059 | s->intr_update = omap_dma_interrupts_4_update; |
2060 | + | ||
1824 | omap_dma_setcaps(s); | 2061 | omap_dma_setcaps(s); |
1825 | - s->tm = qemu_new_timer(vm_clock, (QEMUTimerCB *) omap_dma_channel_run, s); | ||
1826 | omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); | 2062 | omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); |
1827 | - mpu->drq = qemu_allocate_irqs(omap_dma_request, s, 64); | ||
1828 | - omap_dma_reset(s); | ||
1829 | - omap_dma_clk_update(s, 0, 1); | 2063 | + omap_dma_reset(s->dma); |
2064 | + omap_dma_clk_update(s, 0, !!s->dma->freq); | ||
1830 | 2065 | ||
1831 | iomemtype = cpu_register_io_memory(0, omap_dma4_readfn, | 2066 | iomemtype = cpu_register_io_memory(0, omap_dma4_readfn, |
1832 | omap_dma4_writefn, s); | 2067 | omap_dma4_writefn, s); |
1833 | cpu_register_physical_memory(s->base, 0x1000, iomemtype); | 2068 | cpu_register_physical_memory(s->base, 0x1000, iomemtype); |
1834 | 2069 | ||
1835 | - return s; | 2070 | + mpu->drq = s->dma->drq; |
2071 | + | ||
2072 | + return s->dma; | ||
1836 | } | 2073 | } |
1837 | 2074 | ||
1838 | -struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct omap_dma_s *s) | 2075 | +struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct soc_dma_s *dma) |
1839 | { | 2076 | { |
2077 | + struct omap_dma_s *s = dma->opaque; | ||
2078 | + | ||
1840 | return &s->lcd_ch; | 2079 | return &s->lcd_ch; |
1841 | } | 2080 | } |
hw/soc_dma.c
0 โ 100644
1 | +/* | ||
2 | + * On-chip DMA controller framework. | ||
3 | + * | ||
4 | + * Copyright (C) 2008 Nokia Corporation | ||
5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
6 | + * | ||
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. | ||
11 | + * | ||
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. | ||
16 | + * | ||
17 | + * You should have received a copy of the GNU General Public License | ||
18 | + * along with this program; if not, write to the Free Software | ||
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
20 | + * MA 02111-1307 USA | ||
21 | + */ | ||
22 | +#include "qemu-common.h" | ||
23 | +#include "qemu-timer.h" | ||
24 | +#include "soc_dma.h" | ||
25 | + | ||
26 | +void transfer_mem2mem(struct soc_dma_ch_s *ch) | ||
27 | +{ | ||
28 | + memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); | ||
29 | + ch->paddr[0] += ch->bytes; | ||
30 | + ch->paddr[1] += ch->bytes; | ||
31 | +} | ||
32 | + | ||
33 | +void transfer_mem2fifo(struct soc_dma_ch_s *ch) | ||
34 | +{ | ||
35 | + ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); | ||
36 | + ch->paddr[0] += ch->bytes; | ||
37 | +} | ||
38 | + | ||
39 | +void transfer_fifo2mem(struct soc_dma_ch_s *ch) | ||
40 | +{ | ||
41 | + ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); | ||
42 | + ch->paddr[1] += ch->bytes; | ||
43 | +} | ||
44 | + | ||
45 | +/* This is further optimisable but isn't very important because often | ||
46 | + * DMA peripherals forbid this kind of transfers and even when they don't, | ||
47 | + * oprating systems may not need to use them. */ | ||
48 | +static void *fifo_buf; | ||
49 | +static int fifo_size; | ||
50 | +void transfer_fifo2fifo(struct soc_dma_ch_s *ch) | ||
51 | +{ | ||
52 | + if (ch->bytes < fifo_size) | ||
53 | + fifo_buf = realloc(fifo_buf, fifo_size = ch->bytes); | ||
54 | + | ||
55 | + /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ | ||
56 | + ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); | ||
57 | + ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); | ||
58 | +} | ||
59 | + | ||
60 | +struct dma_s { | ||
61 | + struct soc_dma_s soc; | ||
62 | + int chnum; | ||
63 | + uint64_t ch_enable_mask; | ||
64 | + int64_t channel_freq; | ||
65 | + int enabled_count; | ||
66 | + | ||
67 | + struct memmap_entry_s { | ||
68 | + enum soc_dma_port_type type; | ||
69 | + target_phys_addr_t addr; | ||
70 | + union { | ||
71 | + struct { | ||
72 | + void *opaque; | ||
73 | + soc_dma_io_t fn; | ||
74 | + int out; | ||
75 | + } fifo; | ||
76 | + struct { | ||
77 | + void *base; | ||
78 | + size_t size; | ||
79 | + } mem; | ||
80 | + } u; | ||
81 | + } *memmap; | ||
82 | + int memmap_size; | ||
83 | + | ||
84 | + struct soc_dma_ch_s ch[0]; | ||
85 | +}; | ||
86 | + | ||
87 | +static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) | ||
88 | +{ | ||
89 | + int64_t now = qemu_get_clock(vm_clock); | ||
90 | + struct dma_s *dma = (struct dma_s *) ch->dma; | ||
91 | + | ||
92 | + qemu_mod_timer(ch->timer, now + delay_bytes / dma->channel_freq); | ||
93 | +} | ||
94 | + | ||
95 | +static void soc_dma_ch_run(void *opaque) | ||
96 | +{ | ||
97 | + struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque; | ||
98 | + | ||
99 | + ch->running = 1; | ||
100 | + ch->dma->setup_fn(ch); | ||
101 | + ch->transfer_fn(ch); | ||
102 | + ch->running = 0; | ||
103 | + | ||
104 | + if (ch->enable) | ||
105 | + soc_dma_ch_schedule(ch, ch->bytes); | ||
106 | + ch->bytes = 0; | ||
107 | +} | ||
108 | + | ||
109 | +static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma, | ||
110 | + target_phys_addr_t addr) | ||
111 | +{ | ||
112 | + struct memmap_entry_s *lo; | ||
113 | + int hi; | ||
114 | + | ||
115 | + lo = dma->memmap; | ||
116 | + hi = dma->memmap_size; | ||
117 | + | ||
118 | + while (hi > 1) { | ||
119 | + hi /= 2; | ||
120 | + if (lo[hi].addr <= addr) | ||
121 | + lo += hi; | ||
122 | + } | ||
123 | + | ||
124 | + return lo; | ||
125 | +} | ||
126 | + | ||
127 | +static inline enum soc_dma_port_type soc_dma_ch_update_type( | ||
128 | + struct soc_dma_ch_s *ch, int port) | ||
129 | +{ | ||
130 | + struct dma_s *dma = (struct dma_s *) ch->dma; | ||
131 | + struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); | ||
132 | + | ||
133 | + if (entry->type == soc_dma_port_fifo) { | ||
134 | + while (entry < dma->memmap + dma->memmap_size && | ||
135 | + entry->u.fifo.out != port) | ||
136 | + entry ++; | ||
137 | + if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) | ||
138 | + return soc_dma_port_other; | ||
139 | + | ||
140 | + if (ch->type[port] != soc_dma_access_const) | ||
141 | + return soc_dma_port_other; | ||
142 | + | ||
143 | + ch->io_fn[port] = entry->u.fifo.fn; | ||
144 | + ch->io_opaque[port] = entry->u.fifo.opaque; | ||
145 | + return soc_dma_port_fifo; | ||
146 | + } else if (entry->type == soc_dma_port_mem) { | ||
147 | + if (entry->addr > ch->vaddr[port] || | ||
148 | + entry->addr + entry->u.mem.size <= ch->vaddr[port]) | ||
149 | + return soc_dma_port_other; | ||
150 | + | ||
151 | + /* TODO: support constant memory address for source port as used for | ||
152 | + * drawing solid rectangles by PalmOS(R). */ | ||
153 | + if (ch->type[port] != soc_dma_access_const) | ||
154 | + return soc_dma_port_other; | ||
155 | + | ||
156 | + ch->paddr[port] = (uint8_t *) entry->u.mem.base + | ||
157 | + (ch->vaddr[port] - entry->addr); | ||
158 | + /* TODO: save bytes left to the end of the mapping somewhere so we | ||
159 | + * can check we're not reading beyond it. */ | ||
160 | + return soc_dma_port_mem; | ||
161 | + } else | ||
162 | + return soc_dma_port_other; | ||
163 | +} | ||
164 | + | ||
165 | +void soc_dma_ch_update(struct soc_dma_ch_s *ch) | ||
166 | +{ | ||
167 | + enum soc_dma_port_type src, dst; | ||
168 | + | ||
169 | + src = soc_dma_ch_update_type(ch, 0); | ||
170 | + if (src == soc_dma_port_other) { | ||
171 | + ch->update = 0; | ||
172 | + ch->transfer_fn = ch->dma->transfer_fn; | ||
173 | + return; | ||
174 | + } | ||
175 | + dst = soc_dma_ch_update_type(ch, 1); | ||
176 | + | ||
177 | + /* TODO: use src and dst as array indices. */ | ||
178 | + if (src == soc_dma_port_mem && dst == soc_dma_port_mem) | ||
179 | + ch->transfer_fn = transfer_mem2mem; | ||
180 | + else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) | ||
181 | + ch->transfer_fn = transfer_mem2fifo; | ||
182 | + else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) | ||
183 | + ch->transfer_fn = transfer_fifo2mem; | ||
184 | + else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) | ||
185 | + ch->transfer_fn = transfer_fifo2fifo; | ||
186 | + else | ||
187 | + ch->transfer_fn = ch->dma->transfer_fn; | ||
188 | + | ||
189 | + ch->update = (dst != soc_dma_port_other); | ||
190 | +} | ||
191 | + | ||
192 | +static void soc_dma_ch_freq_update(struct dma_s *s) | ||
193 | +{ | ||
194 | + if (s->enabled_count) | ||
195 | + /* We completely ignore channel priorities and stuff */ | ||
196 | + s->channel_freq = s->soc.freq / s->enabled_count; | ||
197 | + else | ||
198 | + /* TODO: Signal that we want to disable the functional clock and let | ||
199 | + * the platform code decide what to do with it, i.e. check that | ||
200 | + * auto-idle is enabled in the clock controller and if we are stopping | ||
201 | + * the clock, do the same with any parent clocks that had only one | ||
202 | + * user keeping them on and auto-idle enabled. */; | ||
203 | +} | ||
204 | + | ||
205 | +void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) | ||
206 | +{ | ||
207 | + struct dma_s *dma = (struct dma_s *) ch->dma; | ||
208 | + | ||
209 | + dma->enabled_count += level - ch->enable; | ||
210 | + | ||
211 | + if (level) | ||
212 | + dma->ch_enable_mask |= 1 << ch->num; | ||
213 | + else | ||
214 | + dma->ch_enable_mask &= ~(1 << ch->num); | ||
215 | + | ||
216 | + if (level != ch->enable) { | ||
217 | + soc_dma_ch_freq_update(dma); | ||
218 | + ch->enable = level; | ||
219 | + | ||
220 | + if (!ch->enable) | ||
221 | + qemu_del_timer(ch->timer); | ||
222 | + else if (!ch->running) | ||
223 | + soc_dma_ch_run(ch); | ||
224 | + else | ||
225 | + soc_dma_ch_schedule(ch, 1); | ||
226 | + } | ||
227 | +} | ||
228 | + | ||
229 | +void soc_dma_reset(struct soc_dma_s *soc) | ||
230 | +{ | ||
231 | + struct dma_s *s = (struct dma_s *) soc; | ||
232 | + | ||
233 | + s->soc.drqbmp = 0; | ||
234 | + s->ch_enable_mask = 0; | ||
235 | + s->enabled_count = 0; | ||
236 | + soc_dma_ch_freq_update(s); | ||
237 | +} | ||
238 | + | ||
239 | +/* TODO: take a functional-clock argument */ | ||
240 | +struct soc_dma_s *soc_dma_init(int n) | ||
241 | +{ | ||
242 | + int i; | ||
243 | + struct dma_s *s = qemu_mallocz(sizeof(*s) + n * sizeof(*s->ch)); | ||
244 | + | ||
245 | + s->chnum = n; | ||
246 | + s->soc.ch = s->ch; | ||
247 | + for (i = 0; i < n; i ++) { | ||
248 | + s->ch[i].dma = &s->soc; | ||
249 | + s->ch[i].num = i; | ||
250 | + s->ch[i].timer = qemu_new_timer(vm_clock, soc_dma_ch_run, &s->ch[i]); | ||
251 | + } | ||
252 | + | ||
253 | + soc_dma_reset(&s->soc); | ||
254 | + | ||
255 | + return &s->soc; | ||
256 | +} | ||
257 | + | ||
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) | ||
260 | +{ | ||
261 | + struct memmap_entry_s *entry; | ||
262 | + struct dma_s *dma = (struct dma_s *) soc; | ||
263 | + | ||
264 | + dma->memmap = realloc(dma->memmap, sizeof(*entry) * | ||
265 | + (dma->memmap_size + 1)); | ||
266 | + entry = soc_dma_lookup(dma, virt_base); | ||
267 | + | ||
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)); | ||
278 | + exit(-1); | ||
279 | + } | ||
280 | + | ||
281 | + if (entry->addr <= virt_base) | ||
282 | + entry ++; | ||
283 | + } else | ||
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); | ||
291 | + exit(-1); | ||
292 | + } | ||
293 | + | ||
294 | + entry ++; | ||
295 | + } | ||
296 | + | ||
297 | + memmove(entry + 1, entry, | ||
298 | + (uint8_t *) (dma->memmap + dma->memmap_size ++) - | ||
299 | + (uint8_t *) entry); | ||
300 | + } else | ||
301 | + dma->memmap_size ++; | ||
302 | + | ||
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; | ||
308 | +} | ||
309 | + | ||
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) | ||
312 | +{ | ||
313 | + struct memmap_entry_s *entry; | ||
314 | + struct dma_s *dma = (struct dma_s *) soc; | ||
315 | + | ||
316 | + dma->memmap = realloc(dma->memmap, sizeof(*entry) * | ||
317 | + (dma->memmap_size + 1)); | ||
318 | + entry = soc_dma_lookup(dma, virt_base); | ||
319 | + | ||
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)); | ||
332 | + exit(-1); | ||
333 | + } | ||
334 | + | ||
335 | + if (entry->addr <= virt_base) | ||
336 | + entry ++; | ||
337 | + } else { | ||
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 | ||
342 | + "\n", __FUNCTION__, | ||
343 | + (target_ulong) virt_base, | ||
344 | + (target_ulong) (virt_base + size), | ||
345 | + (target_ulong) entry->addr); | ||
346 | + exit(-1); | ||
347 | + } | ||
348 | + | ||
349 | + while (entry < dma->memmap + dma->memmap_size && | ||
350 | + entry->addr <= virt_base) | ||
351 | + entry ++; | ||
352 | + } | ||
353 | + | ||
354 | + memmove(entry + 1, entry, | ||
355 | + (uint8_t *) (dma->memmap + dma->memmap_size ++) - | ||
356 | + (uint8_t *) entry); | ||
357 | + } else | ||
358 | + dma->memmap_size ++; | ||
359 | + | ||
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; | ||
364 | +} | ||
365 | + | ||
366 | +/* TODO: port removal for ports like PCMCIA memory */ |
hw/soc_dma.h
0 โ 100644
1 | +/* | ||
2 | + * On-chip DMA controller framework. | ||
3 | + * | ||
4 | + * Copyright (C) 2008 Nokia Corporation | ||
5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
6 | + * | ||
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. | ||
11 | + * | ||
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. | ||
16 | + * | ||
17 | + * You should have received a copy of the GNU General Public License | ||
18 | + * along with this program; if not, write to the Free Software | ||
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
20 | + * MA 02111-1307 USA | ||
21 | + */ | ||
22 | + | ||
23 | +struct soc_dma_s; | ||
24 | +struct soc_dma_ch_s; | ||
25 | +typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len); | ||
26 | +typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch); | ||
27 | + | ||
28 | +enum soc_dma_port_type { | ||
29 | + soc_dma_port_mem, | ||
30 | + soc_dma_port_fifo, | ||
31 | + soc_dma_port_other, | ||
32 | +}; | ||
33 | + | ||
34 | +enum soc_dma_access_type { | ||
35 | + soc_dma_access_const, | ||
36 | + soc_dma_access_linear, | ||
37 | + soc_dma_access_other, | ||
38 | +}; | ||
39 | + | ||
40 | +struct soc_dma_ch_s { | ||
41 | + /* Private */ | ||
42 | + struct soc_dma_s *dma; | ||
43 | + int num; | ||
44 | + QEMUTimer *timer; | ||
45 | + | ||
46 | + /* Set by soc_dma.c */ | ||
47 | + int enable; | ||
48 | + int update; | ||
49 | + | ||
50 | + /* This should be set by dma->setup_fn(). */ | ||
51 | + int bytes; | ||
52 | + /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ | ||
53 | + enum soc_dma_access_type type[2]; | ||
54 | + target_phys_addr_t vaddr[2]; /* Updated by .transfer_fn(). */ | ||
55 | + /* Private */ | ||
56 | + void *paddr[2]; | ||
57 | + soc_dma_io_t io_fn[2]; | ||
58 | + void *io_opaque[2]; | ||
59 | + | ||
60 | + int running; | ||
61 | + soc_dma_transfer_t transfer_fn; | ||
62 | + | ||
63 | + /* Set and used by the DMA module. */ | ||
64 | + void *opaque; | ||
65 | +}; | ||
66 | + | ||
67 | +struct soc_dma_s { | ||
68 | + /* Following fields are set by the SoC DMA module and can be used | ||
69 | + * by anybody. */ | ||
70 | + uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ | ||
71 | + qemu_irq *drq; | ||
72 | + void *opaque; | ||
73 | + int64_t freq; | ||
74 | + soc_dma_transfer_t transfer_fn; | ||
75 | + soc_dma_transfer_t setup_fn; | ||
76 | + /* Set by soc_dma_init() for use by the DMA module. */ | ||
77 | + struct soc_dma_ch_s *ch; | ||
78 | +}; | ||
79 | + | ||
80 | +/* Call to activate or stop a DMA channel. */ | ||
81 | +void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); | ||
82 | +/* Call after every write to one of the following fields and before | ||
83 | + * calling soc_dma_set_request(ch, 1): | ||
84 | + * ch->type[0...1], | ||
85 | + * ch->vaddr[0...1], | ||
86 | + * ch->paddr[0...1], | ||
87 | + * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */ | ||
88 | +void soc_dma_ch_update(struct soc_dma_ch_s *ch); | ||
89 | + | ||
90 | +/* The SoC should call this when the DMA module is being reset. */ | ||
91 | +void soc_dma_reset(struct soc_dma_s *s); | ||
92 | +struct soc_dma_s *soc_dma_init(int n); | ||
93 | + | ||
94 | +void soc_dma_port_add_fifo(struct soc_dma_s *dma, target_phys_addr_t virt_base, | ||
95 | + soc_dma_io_t fn, void *opaque, int out); | ||
96 | +void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, | ||
97 | + target_phys_addr_t virt_base, size_t size); | ||
98 | + | ||
99 | +static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma, | ||
100 | + target_phys_addr_t virt_base, soc_dma_io_t fn, void *opaque) | ||
101 | +{ | ||
102 | + return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0); | ||
103 | +} | ||
104 | + | ||
105 | +static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma, | ||
106 | + target_phys_addr_t virt_base, soc_dma_io_t fn, void *opaque) | ||
107 | +{ | ||
108 | + return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1); | ||
109 | +} | ||
110 | + | ||
111 | +static inline void soc_dma_port_add_mem_ram(struct soc_dma_s *dma, | ||
112 | + ram_addr_t offset, target_phys_addr_t virt_base, size_t size) | ||
113 | +{ | ||
114 | + return soc_dma_port_add_mem(dma, phys_ram_base + offset, virt_base, size); | ||
115 | +} |