|
1 /* |
|
2 * QEMU JAZZ RC4030 chipset |
|
3 * |
|
4 * Copyright (c) 2007-2008 Hervé Poussineau |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 #include "hw.h" |
|
26 #include "mips.h" |
|
27 #include "qemu-timer.h" |
|
28 |
|
29 //#define DEBUG_RC4030 |
|
30 |
|
31 #ifdef DEBUG_RC4030 |
|
32 static const char* irq_names[] = { "parallel", "floppy", "sound", "video", |
|
33 "network", "scsi", "keyboard", "mouse", "serial0", "serial1" }; |
|
34 #endif |
|
35 |
|
36 typedef struct rc4030State |
|
37 { |
|
38 uint32_t config; /* 0x0000: RC4030 config register */ |
|
39 uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ |
|
40 |
|
41 /* DMA */ |
|
42 uint32_t dma_regs[8][4]; |
|
43 uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */ |
|
44 uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */ |
|
45 |
|
46 /* cache */ |
|
47 uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */ |
|
48 uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */ |
|
49 uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */ |
|
50 uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */ |
|
51 uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ |
|
52 uint32_t cache_bwin; /* 0x0060: I/O Cache Buffer Window */ |
|
53 |
|
54 uint32_t offset208; |
|
55 uint32_t offset210; |
|
56 uint32_t nvram_protect; /* 0x0220: NV ram protect register */ |
|
57 uint32_t offset238; |
|
58 uint32_t rem_speed[15]; |
|
59 uint32_t imr_jazz; /* Local bus int enable mask */ |
|
60 uint32_t isr_jazz; /* Local bus int source */ |
|
61 |
|
62 /* timer */ |
|
63 QEMUTimer *periodic_timer; |
|
64 uint32_t itr; /* Interval timer reload */ |
|
65 |
|
66 uint32_t dummy32; |
|
67 qemu_irq timer_irq; |
|
68 qemu_irq jazz_bus_irq; |
|
69 } rc4030State; |
|
70 |
|
71 static void set_next_tick(rc4030State *s) |
|
72 { |
|
73 qemu_irq_lower(s->timer_irq); |
|
74 uint32_t tm_hz; |
|
75 |
|
76 tm_hz = 1000 / (s->itr + 1); |
|
77 |
|
78 qemu_mod_timer(s->periodic_timer, qemu_get_clock(vm_clock) + ticks_per_sec / tm_hz); |
|
79 } |
|
80 |
|
81 /* called for accesses to rc4030 */ |
|
82 static uint32_t rc4030_readl(void *opaque, target_phys_addr_t addr) |
|
83 { |
|
84 rc4030State *s = opaque; |
|
85 uint32_t val; |
|
86 |
|
87 addr &= 0x3fff; |
|
88 switch (addr & ~0x3) { |
|
89 /* Global config register */ |
|
90 case 0x0000: |
|
91 val = s->config; |
|
92 break; |
|
93 /* Invalid Address register */ |
|
94 case 0x0010: |
|
95 val = s->invalid_address_register; |
|
96 break; |
|
97 /* DMA transl. table base */ |
|
98 case 0x0018: |
|
99 val = s->dma_tl_base; |
|
100 break; |
|
101 /* DMA transl. table limit */ |
|
102 case 0x0020: |
|
103 val = s->dma_tl_limit; |
|
104 break; |
|
105 /* Remote Failed Address */ |
|
106 case 0x0038: |
|
107 val = s->remote_failed_address; |
|
108 break; |
|
109 /* Memory Failed Address */ |
|
110 case 0x0040: |
|
111 val = s->memory_failed_address; |
|
112 break; |
|
113 /* I/O Cache Byte Mask */ |
|
114 case 0x0058: |
|
115 val = s->cache_bmask; |
|
116 /* HACK */ |
|
117 if (s->cache_bmask == (uint32_t)-1) |
|
118 s->cache_bmask = 0; |
|
119 break; |
|
120 /* Remote Speed Registers */ |
|
121 case 0x0070: |
|
122 case 0x0078: |
|
123 case 0x0080: |
|
124 case 0x0088: |
|
125 case 0x0090: |
|
126 case 0x0098: |
|
127 case 0x00a0: |
|
128 case 0x00a8: |
|
129 case 0x00b0: |
|
130 case 0x00b8: |
|
131 case 0x00c0: |
|
132 case 0x00c8: |
|
133 case 0x00d0: |
|
134 case 0x00d8: |
|
135 case 0x00e0: |
|
136 val = s->rem_speed[(addr - 0x0070) >> 3]; |
|
137 break; |
|
138 /* DMA channel base address */ |
|
139 case 0x0100: |
|
140 case 0x0108: |
|
141 case 0x0110: |
|
142 case 0x0118: |
|
143 case 0x0120: |
|
144 case 0x0128: |
|
145 case 0x0130: |
|
146 case 0x0138: |
|
147 case 0x0140: |
|
148 case 0x0148: |
|
149 case 0x0150: |
|
150 case 0x0158: |
|
151 case 0x0160: |
|
152 case 0x0168: |
|
153 case 0x0170: |
|
154 case 0x0178: |
|
155 case 0x0180: |
|
156 case 0x0188: |
|
157 case 0x0190: |
|
158 case 0x0198: |
|
159 case 0x01a0: |
|
160 case 0x01a8: |
|
161 case 0x01b0: |
|
162 case 0x01b8: |
|
163 case 0x01c0: |
|
164 case 0x01c8: |
|
165 case 0x01d0: |
|
166 case 0x01d8: |
|
167 case 0x01e0: |
|
168 case 0x1e8: |
|
169 case 0x01f0: |
|
170 case 0x01f8: |
|
171 { |
|
172 int entry = (addr - 0x0100) >> 5; |
|
173 int idx = (addr & 0x1f) >> 3; |
|
174 val = s->dma_regs[entry][idx]; |
|
175 } |
|
176 break; |
|
177 /* Offset 0x0208 */ |
|
178 case 0x0208: |
|
179 val = s->offset208; |
|
180 break; |
|
181 /* Offset 0x0210 */ |
|
182 case 0x0210: |
|
183 val = s->offset210; |
|
184 break; |
|
185 /* NV ram protect register */ |
|
186 case 0x0220: |
|
187 val = s->nvram_protect; |
|
188 break; |
|
189 /* Interval timer count */ |
|
190 case 0x0230: |
|
191 val = s->dummy32; |
|
192 qemu_irq_lower(s->timer_irq); |
|
193 break; |
|
194 /* Offset 0x0238 */ |
|
195 case 0x0238: |
|
196 val = s->offset238; |
|
197 break; |
|
198 default: |
|
199 #ifdef DEBUG_RC4030 |
|
200 printf("rc4030: invalid read [" TARGET_FMT_lx "]\n", addr); |
|
201 #endif |
|
202 val = 0; |
|
203 break; |
|
204 } |
|
205 |
|
206 #ifdef DEBUG_RC4030 |
|
207 if ((addr & ~3) != 0x230) |
|
208 printf("rc4030: read 0x%02x at " TARGET_FMT_lx "\n", val, addr); |
|
209 #endif |
|
210 |
|
211 return val; |
|
212 } |
|
213 |
|
214 static uint32_t rc4030_readw(void *opaque, target_phys_addr_t addr) |
|
215 { |
|
216 uint32_t v = rc4030_readl(opaque, addr & ~0x3); |
|
217 if (addr & 0x2) |
|
218 return v >> 16; |
|
219 else |
|
220 return v & 0xffff; |
|
221 } |
|
222 |
|
223 static uint32_t rc4030_readb(void *opaque, target_phys_addr_t addr) |
|
224 { |
|
225 uint32_t v = rc4030_readl(opaque, addr & ~0x3); |
|
226 return (v >> (8 * (addr & 0x3))) & 0xff; |
|
227 } |
|
228 |
|
229 static void rc4030_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
230 { |
|
231 rc4030State *s = opaque; |
|
232 addr &= 0x3fff; |
|
233 |
|
234 #ifdef DEBUG_RC4030 |
|
235 printf("rc4030: write 0x%02x at " TARGET_FMT_lx "\n", val, addr); |
|
236 #endif |
|
237 |
|
238 switch (addr & ~0x3) { |
|
239 /* Global config register */ |
|
240 case 0x0000: |
|
241 s->config = val; |
|
242 break; |
|
243 /* DMA transl. table base */ |
|
244 case 0x0018: |
|
245 s->dma_tl_base = val; |
|
246 break; |
|
247 /* DMA transl. table limit */ |
|
248 case 0x0020: |
|
249 s->dma_tl_limit = val; |
|
250 break; |
|
251 /* I/O Cache Physical Tag */ |
|
252 case 0x0048: |
|
253 s->cache_ptag = val; |
|
254 break; |
|
255 /* I/O Cache Logical Tag */ |
|
256 case 0x0050: |
|
257 s->cache_ltag = val; |
|
258 break; |
|
259 /* I/O Cache Byte Mask */ |
|
260 case 0x0058: |
|
261 s->cache_bmask |= val; /* HACK */ |
|
262 break; |
|
263 /* I/O Cache Buffer Window */ |
|
264 case 0x0060: |
|
265 s->cache_bwin = val; |
|
266 /* HACK */ |
|
267 if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { |
|
268 target_phys_addr_t dests[] = { 4, 0, 8, 0x10 }; |
|
269 static int current = 0; |
|
270 target_phys_addr_t dest = 0 + dests[current]; |
|
271 uint8_t buf; |
|
272 current = (current + 1) % (ARRAY_SIZE(dests)); |
|
273 buf = s->cache_bwin - 1; |
|
274 cpu_physical_memory_rw(dest, &buf, 1, 1); |
|
275 } |
|
276 break; |
|
277 /* Remote Speed Registers */ |
|
278 case 0x0070: |
|
279 case 0x0078: |
|
280 case 0x0080: |
|
281 case 0x0088: |
|
282 case 0x0090: |
|
283 case 0x0098: |
|
284 case 0x00a0: |
|
285 case 0x00a8: |
|
286 case 0x00b0: |
|
287 case 0x00b8: |
|
288 case 0x00c0: |
|
289 case 0x00c8: |
|
290 case 0x00d0: |
|
291 case 0x00d8: |
|
292 case 0x00e0: |
|
293 s->rem_speed[(addr - 0x0070) >> 3] = val; |
|
294 break; |
|
295 /* DMA channel base address */ |
|
296 case 0x0100: |
|
297 case 0x0108: |
|
298 case 0x0110: |
|
299 case 0x0118: |
|
300 case 0x0120: |
|
301 case 0x0128: |
|
302 case 0x0130: |
|
303 case 0x0138: |
|
304 case 0x0140: |
|
305 case 0x0148: |
|
306 case 0x0150: |
|
307 case 0x0158: |
|
308 case 0x0160: |
|
309 case 0x0168: |
|
310 case 0x0170: |
|
311 case 0x0178: |
|
312 case 0x0180: |
|
313 case 0x0188: |
|
314 case 0x0190: |
|
315 case 0x0198: |
|
316 case 0x01a0: |
|
317 case 0x01a8: |
|
318 case 0x01b0: |
|
319 case 0x01b8: |
|
320 case 0x01c0: |
|
321 case 0x01c8: |
|
322 case 0x01d0: |
|
323 case 0x01d8: |
|
324 case 0x01e0: |
|
325 case 0x1e8: |
|
326 case 0x01f0: |
|
327 case 0x01f8: |
|
328 { |
|
329 int entry = (addr - 0x0100) >> 5; |
|
330 int idx = (addr & 0x1f) >> 3; |
|
331 s->dma_regs[entry][idx] = val; |
|
332 } |
|
333 break; |
|
334 /* Offset 0x0210 */ |
|
335 case 0x0210: |
|
336 s->offset210 = val; |
|
337 break; |
|
338 /* Interval timer reload */ |
|
339 case 0x0228: |
|
340 s->itr = val; |
|
341 qemu_irq_lower(s->timer_irq); |
|
342 set_next_tick(s); |
|
343 break; |
|
344 default: |
|
345 #ifdef DEBUG_RC4030 |
|
346 printf("rc4030: invalid write of 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); |
|
347 #endif |
|
348 break; |
|
349 } |
|
350 } |
|
351 |
|
352 static void rc4030_writew(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
353 { |
|
354 uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); |
|
355 |
|
356 if (addr & 0x2) |
|
357 val = (val << 16) | (old_val & 0x0000ffff); |
|
358 else |
|
359 val = val | (old_val & 0xffff0000); |
|
360 rc4030_writel(opaque, addr & ~0x3, val); |
|
361 } |
|
362 |
|
363 static void rc4030_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
364 { |
|
365 uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); |
|
366 |
|
367 switch (addr & 3) { |
|
368 case 0: |
|
369 val = val | (old_val & 0xffffff00); |
|
370 break; |
|
371 case 1: |
|
372 val = (val << 8) | (old_val & 0xffff00ff); |
|
373 break; |
|
374 case 2: |
|
375 val = (val << 16) | (old_val & 0xff00ffff); |
|
376 break; |
|
377 case 3: |
|
378 val = (val << 24) | (old_val & 0x00ffffff); |
|
379 break; |
|
380 } |
|
381 rc4030_writel(opaque, addr & ~0x3, val); |
|
382 } |
|
383 |
|
384 static CPUReadMemoryFunc *rc4030_read[3] = { |
|
385 rc4030_readb, |
|
386 rc4030_readw, |
|
387 rc4030_readl, |
|
388 }; |
|
389 |
|
390 static CPUWriteMemoryFunc *rc4030_write[3] = { |
|
391 rc4030_writeb, |
|
392 rc4030_writew, |
|
393 rc4030_writel, |
|
394 }; |
|
395 |
|
396 static void update_jazz_irq(rc4030State *s) |
|
397 { |
|
398 uint16_t pending; |
|
399 |
|
400 pending = s->isr_jazz & s->imr_jazz; |
|
401 |
|
402 #ifdef DEBUG_RC4030 |
|
403 if (s->isr_jazz != 0) { |
|
404 uint32_t irq = 0; |
|
405 printf("jazz pending:"); |
|
406 for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) { |
|
407 if (s->isr_jazz & (1 << irq)) { |
|
408 printf(" %s", irq_names[irq]); |
|
409 if (!(s->imr_jazz & (1 << irq))) { |
|
410 printf("(ignored)"); |
|
411 } |
|
412 } |
|
413 } |
|
414 printf("\n"); |
|
415 } |
|
416 #endif |
|
417 |
|
418 if (pending != 0) |
|
419 qemu_irq_raise(s->jazz_bus_irq); |
|
420 else |
|
421 qemu_irq_lower(s->jazz_bus_irq); |
|
422 } |
|
423 |
|
424 static void rc4030_irq_jazz_request(void *opaque, int irq, int level) |
|
425 { |
|
426 rc4030State *s = opaque; |
|
427 |
|
428 if (level) { |
|
429 s->isr_jazz |= 1 << irq; |
|
430 } else { |
|
431 s->isr_jazz &= ~(1 << irq); |
|
432 } |
|
433 |
|
434 update_jazz_irq(s); |
|
435 } |
|
436 |
|
437 static void rc4030_periodic_timer(void *opaque) |
|
438 { |
|
439 rc4030State *s = opaque; |
|
440 |
|
441 set_next_tick(s); |
|
442 qemu_irq_raise(s->timer_irq); |
|
443 } |
|
444 |
|
445 static uint32_t int_readb(void *opaque, target_phys_addr_t addr) |
|
446 { |
|
447 rc4030State *s = opaque; |
|
448 uint32_t val; |
|
449 uint32_t irq; |
|
450 addr &= 0xfff; |
|
451 |
|
452 switch (addr) { |
|
453 case 0x00: { |
|
454 /* Local bus int source */ |
|
455 uint32_t pending = s->isr_jazz & s->imr_jazz; |
|
456 val = 0; |
|
457 irq = 0; |
|
458 while (pending) { |
|
459 if (pending & 1) { |
|
460 //printf("returning irq %s\n", irq_names[irq]); |
|
461 val = (irq + 1) << 2; |
|
462 break; |
|
463 } |
|
464 irq++; |
|
465 pending >>= 1; |
|
466 } |
|
467 break; |
|
468 } |
|
469 default: |
|
470 #ifdef DEBUG_RC4030 |
|
471 printf("rc4030: (interrupt controller) invalid read [" TARGET_FMT_lx "]\n", addr); |
|
472 #endif |
|
473 val = 0; |
|
474 } |
|
475 |
|
476 #ifdef DEBUG_RC4030 |
|
477 printf("rc4030: (interrupt controller) read 0x%02x at " TARGET_FMT_lx "\n", val, addr); |
|
478 #endif |
|
479 |
|
480 return val; |
|
481 } |
|
482 |
|
483 static uint32_t int_readw(void *opaque, target_phys_addr_t addr) |
|
484 { |
|
485 uint32_t v; |
|
486 v = int_readb(opaque, addr); |
|
487 v |= int_readb(opaque, addr + 1) << 8; |
|
488 return v; |
|
489 } |
|
490 |
|
491 static uint32_t int_readl(void *opaque, target_phys_addr_t addr) |
|
492 { |
|
493 uint32_t v; |
|
494 v = int_readb(opaque, addr); |
|
495 v |= int_readb(opaque, addr + 1) << 8; |
|
496 v |= int_readb(opaque, addr + 2) << 16; |
|
497 v |= int_readb(opaque, addr + 3) << 24; |
|
498 return v; |
|
499 } |
|
500 |
|
501 static void int_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
502 { |
|
503 rc4030State *s = opaque; |
|
504 addr &= 0xfff; |
|
505 |
|
506 #ifdef DEBUG_RC4030 |
|
507 printf("rc4030: (interrupt controller) write 0x%02x at " TARGET_FMT_lx "\n", val, addr); |
|
508 #endif |
|
509 |
|
510 switch (addr) { |
|
511 /* Local bus int enable mask */ |
|
512 case 0x02: |
|
513 s->imr_jazz = (s->imr_jazz & 0xff00) | (val << 0); update_jazz_irq(s); |
|
514 break; |
|
515 case 0x03: |
|
516 s->imr_jazz = (s->imr_jazz & 0x00ff) | (val << 8); update_jazz_irq(s); |
|
517 break; |
|
518 default: |
|
519 #ifdef DEBUG_RC4030 |
|
520 printf("rc4030: (interrupt controller) invalid write of 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); |
|
521 #endif |
|
522 break; |
|
523 } |
|
524 } |
|
525 |
|
526 static void int_writew(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
527 { |
|
528 int_writeb(opaque, addr, val & 0xff); |
|
529 int_writeb(opaque, addr + 1, (val >> 8) & 0xff); |
|
530 } |
|
531 |
|
532 static void int_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
533 { |
|
534 int_writeb(opaque, addr, val & 0xff); |
|
535 int_writeb(opaque, addr + 1, (val >> 8) & 0xff); |
|
536 int_writeb(opaque, addr + 2, (val >> 16) & 0xff); |
|
537 int_writeb(opaque, addr + 3, (val >> 24) & 0xff); |
|
538 } |
|
539 |
|
540 static CPUReadMemoryFunc *int_read[3] = { |
|
541 int_readb, |
|
542 int_readw, |
|
543 int_readl, |
|
544 }; |
|
545 |
|
546 static CPUWriteMemoryFunc *int_write[3] = { |
|
547 int_writeb, |
|
548 int_writew, |
|
549 int_writel, |
|
550 }; |
|
551 |
|
552 #define G364_512KB_RAM (0x0) |
|
553 #define G364_2MB_RAM (0x1) |
|
554 #define G364_8MB_RAM (0x2) |
|
555 #define G364_32MB_RAM (0x3) |
|
556 |
|
557 static void rc4030_reset(void *opaque) |
|
558 { |
|
559 rc4030State *s = opaque; |
|
560 int i; |
|
561 |
|
562 s->config = (G364_2MB_RAM << 8) | 0x04; |
|
563 s->invalid_address_register = 0; |
|
564 |
|
565 memset(s->dma_regs, 0, sizeof(s->dma_regs)); |
|
566 s->dma_tl_base = s->dma_tl_limit = 0; |
|
567 |
|
568 s->remote_failed_address = s->memory_failed_address = 0; |
|
569 s->cache_ptag = s->cache_ltag = 0; |
|
570 s->cache_bmask = s->cache_bwin = 0; |
|
571 |
|
572 s->offset208 = 0; |
|
573 s->offset210 = 0x18186; |
|
574 s->nvram_protect = 7; |
|
575 s->offset238 = 7; |
|
576 for (i = 0; i < 15; i++) |
|
577 s->rem_speed[i] = 7; |
|
578 s->imr_jazz = s->isr_jazz = 0; |
|
579 |
|
580 s->itr = 0; |
|
581 s->dummy32 = 0; |
|
582 |
|
583 qemu_irq_lower(s->timer_irq); |
|
584 qemu_irq_lower(s->jazz_bus_irq); |
|
585 } |
|
586 |
|
587 qemu_irq *rc4030_init(qemu_irq timer, qemu_irq jazz_bus) |
|
588 { |
|
589 rc4030State *s; |
|
590 int s_chipset, s_int; |
|
591 |
|
592 s = qemu_mallocz(sizeof(rc4030State)); |
|
593 if (!s) |
|
594 return NULL; |
|
595 |
|
596 s->periodic_timer = qemu_new_timer(vm_clock, rc4030_periodic_timer, s); |
|
597 s->timer_irq = timer; |
|
598 s->jazz_bus_irq = jazz_bus; |
|
599 |
|
600 qemu_register_reset(rc4030_reset, s); |
|
601 rc4030_reset(s); |
|
602 |
|
603 s_chipset = cpu_register_io_memory(0, rc4030_read, rc4030_write, s); |
|
604 cpu_register_physical_memory(0x80000000, 0x300, s_chipset); |
|
605 s_int = cpu_register_io_memory(0, int_read, int_write, s); |
|
606 cpu_register_physical_memory(0xf0000000, 0x00001000, s_int); |
|
607 |
|
608 return qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16); |
|
609 } |