Revision 9ea0b7a1 hw/rc4030.c
b/hw/rc4030.c | ||
---|---|---|
1 | 1 |
/* |
2 | 2 |
* QEMU JAZZ RC4030 chipset |
3 | 3 |
* |
4 |
* Copyright (c) 2007-2008 Hervé Poussineau
|
|
4 |
* Copyright (c) 2007-2009 Herve Poussineau
|
|
5 | 5 |
* |
6 | 6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | 7 |
* of this software and associated documentation files (the "Software"), to deal |
... | ... | |
66 | 66 |
typedef struct rc4030State |
67 | 67 |
{ |
68 | 68 |
uint32_t config; /* 0x0000: RC4030 config register */ |
69 |
uint32_t revision; /* 0x0008: RC4030 Revision register */ |
|
69 | 70 |
uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ |
70 | 71 |
|
71 | 72 |
/* DMA */ |
... | ... | |
74 | 75 |
uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */ |
75 | 76 |
|
76 | 77 |
/* cache */ |
78 |
uint32_t cache_maint; /* 0x0030: Cache Maintenance */ |
|
77 | 79 |
uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */ |
78 | 80 |
uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */ |
79 | 81 |
uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */ |
80 | 82 |
uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */ |
81 | 83 |
uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ |
82 |
uint32_t cache_bwin; /* 0x0060: I/O Cache Buffer Window */ |
|
83 | 84 |
|
85 |
uint32_t nmi_interrupt; /* 0x0200: interrupt source */ |
|
84 | 86 |
uint32_t offset210; |
85 | 87 |
uint32_t nvram_protect; /* 0x0220: NV ram protect register */ |
86 |
uint32_t offset238; |
|
87 |
uint32_t rem_speed[15]; |
|
88 |
uint32_t rem_speed[16]; |
|
88 | 89 |
uint32_t imr_jazz; /* Local bus int enable mask */ |
89 | 90 |
uint32_t isr_jazz; /* Local bus int source */ |
90 | 91 |
|
... | ... | |
118 | 119 |
case 0x0000: |
119 | 120 |
val = s->config; |
120 | 121 |
break; |
122 |
/* Revision register */ |
|
123 |
case 0x0008: |
|
124 |
val = s->revision; |
|
125 |
break; |
|
121 | 126 |
/* Invalid Address register */ |
122 | 127 |
case 0x0010: |
123 | 128 |
val = s->invalid_address_register; |
... | ... | |
161 | 166 |
case 0x00d0: |
162 | 167 |
case 0x00d8: |
163 | 168 |
case 0x00e0: |
169 |
case 0x00e8: |
|
164 | 170 |
val = s->rem_speed[(addr - 0x0070) >> 3]; |
165 | 171 |
break; |
166 | 172 |
/* DMA channel base address */ |
... | ... | |
202 | 208 |
val = s->dma_regs[entry][idx]; |
203 | 209 |
} |
204 | 210 |
break; |
205 |
/* Offset 0x0208 */ |
|
211 |
/* Interrupt source */ |
|
212 |
case 0x0200: |
|
213 |
val = s->nmi_interrupt; |
|
214 |
break; |
|
215 |
/* Error type */ |
|
206 | 216 |
case 0x0208: |
207 | 217 |
val = 0; |
208 | 218 |
break; |
... | ... | |
219 | 229 |
val = 0; |
220 | 230 |
qemu_irq_lower(s->timer_irq); |
221 | 231 |
break; |
222 |
/* Offset 0x0238 */
|
|
232 |
/* EISA interrupt */
|
|
223 | 233 |
case 0x0238: |
224 |
val = s->offset238;
|
|
234 |
val = 7; /* FIXME: should be read from EISA controller */
|
|
225 | 235 |
break; |
226 | 236 |
default: |
227 | 237 |
RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr); |
... | ... | |
275 | 285 |
break; |
276 | 286 |
/* Cache Maintenance */ |
277 | 287 |
case 0x0030: |
278 |
RC4030_ERROR("Cache maintenance not handled yet (val 0x%02x)\n", val);
|
|
288 |
s->cache_maint = val;
|
|
279 | 289 |
break; |
280 | 290 |
/* I/O Cache Physical Tag */ |
281 | 291 |
case 0x0048: |
... | ... | |
291 | 301 |
break; |
292 | 302 |
/* I/O Cache Buffer Window */ |
293 | 303 |
case 0x0060: |
294 |
s->cache_bwin = val; |
|
295 | 304 |
/* HACK */ |
296 | 305 |
if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { |
297 |
target_phys_addr_t dests[] = { 4, 0, 8, 0x10 }; |
|
298 |
static int current = 0; |
|
299 |
target_phys_addr_t dest = 0 + dests[current]; |
|
300 |
uint8_t buf; |
|
301 |
current = (current + 1) % (ARRAY_SIZE(dests)); |
|
302 |
buf = s->cache_bwin - 1; |
|
303 |
cpu_physical_memory_rw(dest, &buf, 1, 1); |
|
306 |
target_phys_addr_t dest = s->cache_ptag & ~0x1; |
|
307 |
dest += (s->cache_maint & 0x3) << 3; |
|
308 |
cpu_physical_memory_rw(dest, (uint8_t*)&val, 4, 1); |
|
304 | 309 |
} |
305 | 310 |
break; |
306 | 311 |
/* Remote Speed Registers */ |
... | ... | |
319 | 324 |
case 0x00d0: |
320 | 325 |
case 0x00d8: |
321 | 326 |
case 0x00e0: |
327 |
case 0x00e8: |
|
322 | 328 |
s->rem_speed[(addr - 0x0070) >> 3] = val; |
323 | 329 |
break; |
324 | 330 |
/* DMA channel base address */ |
... | ... | |
370 | 376 |
qemu_irq_lower(s->timer_irq); |
371 | 377 |
set_next_tick(s); |
372 | 378 |
break; |
379 |
/* EISA interrupt */ |
|
380 |
case 0x0238: |
|
381 |
break; |
|
373 | 382 |
default: |
374 | 383 |
RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr); |
375 | 384 |
break; |
... | ... | |
580 | 589 |
int i; |
581 | 590 |
|
582 | 591 |
s->config = 0x410; /* some boards seem to accept 0x104 too */ |
592 |
s->revision = 1; |
|
583 | 593 |
s->invalid_address_register = 0; |
584 | 594 |
|
585 | 595 |
memset(s->dma_regs, 0, sizeof(s->dma_regs)); |
586 | 596 |
s->dma_tl_base = s->dma_tl_limit = 0; |
587 | 597 |
|
588 | 598 |
s->remote_failed_address = s->memory_failed_address = 0; |
599 |
s->cache_maint = 0; |
|
589 | 600 |
s->cache_ptag = s->cache_ltag = 0; |
590 |
s->cache_bmask = s->cache_bwin = 0;
|
|
601 |
s->cache_bmask = 0; |
|
591 | 602 |
|
592 | 603 |
s->offset210 = 0x18186; |
593 | 604 |
s->nvram_protect = 7; |
594 |
s->offset238 = 7; |
|
595 | 605 |
for (i = 0; i < 15; i++) |
596 | 606 |
s->rem_speed[i] = 7; |
597 |
s->imr_jazz = s->isr_jazz = 0; |
|
607 |
s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */ |
|
608 |
s->isr_jazz = 0; |
|
598 | 609 |
|
599 | 610 |
s->itr = 0; |
600 | 611 |
|
... | ... | |
607 | 618 |
rc4030State* s = opaque; |
608 | 619 |
int i, j; |
609 | 620 |
|
610 |
if (version_id != 1)
|
|
621 |
if (version_id != 2)
|
|
611 | 622 |
return -EINVAL; |
612 | 623 |
|
613 | 624 |
s->config = qemu_get_be32(f); |
... | ... | |
617 | 628 |
s->dma_regs[i][j] = qemu_get_be32(f); |
618 | 629 |
s->dma_tl_base = qemu_get_be32(f); |
619 | 630 |
s->dma_tl_limit = qemu_get_be32(f); |
631 |
s->cache_maint = qemu_get_be32(f); |
|
620 | 632 |
s->remote_failed_address = qemu_get_be32(f); |
621 | 633 |
s->memory_failed_address = qemu_get_be32(f); |
622 | 634 |
s->cache_ptag = qemu_get_be32(f); |
623 | 635 |
s->cache_ltag = qemu_get_be32(f); |
624 | 636 |
s->cache_bmask = qemu_get_be32(f); |
625 |
s->cache_bwin = qemu_get_be32(f); |
|
626 | 637 |
s->offset210 = qemu_get_be32(f); |
627 | 638 |
s->nvram_protect = qemu_get_be32(f); |
628 |
s->offset238 = qemu_get_be32(f); |
|
629 | 639 |
for (i = 0; i < 15; i++) |
630 | 640 |
s->rem_speed[i] = qemu_get_be32(f); |
631 | 641 |
s->imr_jazz = qemu_get_be32(f); |
... | ... | |
650 | 660 |
qemu_put_be32(f, s->dma_regs[i][j]); |
651 | 661 |
qemu_put_be32(f, s->dma_tl_base); |
652 | 662 |
qemu_put_be32(f, s->dma_tl_limit); |
663 |
qemu_put_be32(f, s->cache_maint); |
|
653 | 664 |
qemu_put_be32(f, s->remote_failed_address); |
654 | 665 |
qemu_put_be32(f, s->memory_failed_address); |
655 | 666 |
qemu_put_be32(f, s->cache_ptag); |
656 | 667 |
qemu_put_be32(f, s->cache_ltag); |
657 | 668 |
qemu_put_be32(f, s->cache_bmask); |
658 |
qemu_put_be32(f, s->cache_bwin); |
|
659 | 669 |
qemu_put_be32(f, s->offset210); |
660 | 670 |
qemu_put_be32(f, s->nvram_protect); |
661 |
qemu_put_be32(f, s->offset238); |
|
662 | 671 |
for (i = 0; i < 15; i++) |
663 | 672 |
qemu_put_be32(f, s->rem_speed[i]); |
664 | 673 |
qemu_put_be32(f, s->imr_jazz); |
... | ... | |
666 | 675 |
qemu_put_be32(f, s->itr); |
667 | 676 |
} |
668 | 677 |
|
669 |
static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
|
|
678 |
static void rc4030_dma_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write)
|
|
670 | 679 |
{ |
671 | 680 |
rc4030State *s = opaque; |
672 | 681 |
target_phys_addr_t entry_addr; |
673 |
target_phys_addr_t dma_addr, phys_addr;
|
|
682 |
target_phys_addr_t phys_addr; |
|
674 | 683 |
dma_pagetable_entry entry; |
675 |
int index, dev_to_mem;
|
|
684 |
int index; |
|
676 | 685 |
int ncpy, i; |
677 | 686 |
|
678 |
s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR); |
|
679 |
|
|
680 |
/* Check DMA channel consistency */ |
|
681 |
dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1; |
|
682 |
if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) || |
|
683 |
(is_write != dev_to_mem)) { |
|
684 |
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR; |
|
685 |
return; |
|
686 |
} |
|
687 |
|
|
688 |
if (len > s->dma_regs[n][DMA_REG_COUNT]) |
|
689 |
len = s->dma_regs[n][DMA_REG_COUNT]; |
|
690 |
|
|
691 |
dma_addr = s->dma_regs[n][DMA_REG_ADDRESS]; |
|
692 | 687 |
i = 0; |
693 | 688 |
for (;;) { |
694 | 689 |
if (i == len) { |
695 |
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR; |
|
696 | 690 |
break; |
697 | 691 |
} |
698 | 692 |
|
699 |
ncpy = DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE - 1));
|
|
693 |
ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1)); |
|
700 | 694 |
if (ncpy > len - i) |
701 | 695 |
ncpy = len - i; |
702 | 696 |
|
703 | 697 |
/* Get DMA translation table entry */ |
704 |
index = dma_addr / DMA_PAGESIZE;
|
|
698 |
index = addr / DMA_PAGESIZE; |
|
705 | 699 |
if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) { |
706 |
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR; |
|
707 | 700 |
break; |
708 | 701 |
} |
709 | 702 |
entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry); |
... | ... | |
712 | 705 |
cpu_physical_memory_rw(entry_addr, (uint8_t *)&entry, sizeof(entry), 0); |
713 | 706 |
|
714 | 707 |
/* Read/write data at right place */ |
715 |
phys_addr = entry.frame + (dma_addr & (DMA_PAGESIZE - 1));
|
|
708 |
phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1)); |
|
716 | 709 |
cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write); |
717 | 710 |
|
718 | 711 |
i += ncpy; |
719 |
dma_addr += ncpy; |
|
720 |
s->dma_regs[n][DMA_REG_COUNT] -= ncpy; |
|
712 |
addr += ncpy; |
|
713 |
} |
|
714 |
} |
|
715 |
|
|
716 |
static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write) |
|
717 |
{ |
|
718 |
rc4030State *s = opaque; |
|
719 |
target_phys_addr_t dma_addr; |
|
720 |
int dev_to_mem; |
|
721 |
|
|
722 |
s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR); |
|
723 |
|
|
724 |
/* Check DMA channel consistency */ |
|
725 |
dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1; |
|
726 |
if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) || |
|
727 |
(is_write != dev_to_mem)) { |
|
728 |
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR; |
|
729 |
s->nmi_interrupt |= 1 << n; |
|
730 |
return; |
|
721 | 731 |
} |
722 | 732 |
|
733 |
/* Get start address and len */ |
|
734 |
if (len > s->dma_regs[n][DMA_REG_COUNT]) |
|
735 |
len = s->dma_regs[n][DMA_REG_COUNT]; |
|
736 |
dma_addr = s->dma_regs[n][DMA_REG_ADDRESS]; |
|
737 |
|
|
738 |
/* Read/write data at right place */ |
|
739 |
rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write); |
|
740 |
|
|
741 |
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR; |
|
742 |
s->dma_regs[n][DMA_REG_COUNT] -= len; |
|
743 |
|
|
723 | 744 |
#ifdef DEBUG_RC4030_DMA |
724 | 745 |
{ |
725 | 746 |
int i, j; |
... | ... | |
792 | 813 |
s->jazz_bus_irq = jazz_bus; |
793 | 814 |
|
794 | 815 |
qemu_register_reset(rc4030_reset, s); |
795 |
register_savevm("rc4030", 0, 1, rc4030_save, rc4030_load, s);
|
|
816 |
register_savevm("rc4030", 0, 2, rc4030_save, rc4030_load, s);
|
|
796 | 817 |
rc4030_reset(s); |
797 | 818 |
|
798 | 819 |
s_chipset = cpu_register_io_memory(0, rc4030_read, rc4030_write, s); |
Also available in: Unified diff