linux-user, arm: add syscall table generation support
[qemu.git] / hw / m68k / mcf5206.c
1 /*
2 * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
3 *
4 * Copyright (c) 2007 CodeSourcery.
5 *
6 * This code is licensed under the GPL
7 */
8
9 #include "qemu/osdep.h"
10 #include "qemu/error-report.h"
11 #include "cpu.h"
12 #include "hw/hw.h"
13 #include "hw/irq.h"
14 #include "hw/m68k/mcf.h"
15 #include "qemu/timer.h"
16 #include "hw/ptimer.h"
17 #include "sysemu/sysemu.h"
18
19 /* General purpose timer module. */
20 typedef struct {
21 uint16_t tmr;
22 uint16_t trr;
23 uint16_t tcr;
24 uint16_t ter;
25 ptimer_state *timer;
26 qemu_irq irq;
27 int irq_state;
28 } m5206_timer_state;
29
30 #define TMR_RST 0x01
31 #define TMR_CLK 0x06
32 #define TMR_FRR 0x08
33 #define TMR_ORI 0x10
34 #define TMR_OM 0x20
35 #define TMR_CE 0xc0
36
37 #define TER_CAP 0x01
38 #define TER_REF 0x02
39
40 static void m5206_timer_update(m5206_timer_state *s)
41 {
42 if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
43 qemu_irq_raise(s->irq);
44 else
45 qemu_irq_lower(s->irq);
46 }
47
48 static void m5206_timer_reset(m5206_timer_state *s)
49 {
50 s->tmr = 0;
51 s->trr = 0;
52 }
53
54 static void m5206_timer_recalibrate(m5206_timer_state *s)
55 {
56 int prescale;
57 int mode;
58
59 ptimer_transaction_begin(s->timer);
60 ptimer_stop(s->timer);
61
62 if ((s->tmr & TMR_RST) == 0) {
63 goto exit;
64 }
65
66 prescale = (s->tmr >> 8) + 1;
67 mode = (s->tmr >> 1) & 3;
68 if (mode == 2)
69 prescale *= 16;
70
71 if (mode == 3 || mode == 0)
72 hw_error("m5206_timer: mode %d not implemented\n", mode);
73 if ((s->tmr & TMR_FRR) == 0)
74 hw_error("m5206_timer: free running mode not implemented\n");
75
76 /* Assume 66MHz system clock. */
77 ptimer_set_freq(s->timer, 66000000 / prescale);
78
79 ptimer_set_limit(s->timer, s->trr, 0);
80
81 ptimer_run(s->timer, 0);
82 exit:
83 ptimer_transaction_commit(s->timer);
84 }
85
86 static void m5206_timer_trigger(void *opaque)
87 {
88 m5206_timer_state *s = (m5206_timer_state *)opaque;
89 s->ter |= TER_REF;
90 m5206_timer_update(s);
91 }
92
93 static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
94 {
95 switch (addr) {
96 case 0:
97 return s->tmr;
98 case 4:
99 return s->trr;
100 case 8:
101 return s->tcr;
102 case 0xc:
103 return s->trr - ptimer_get_count(s->timer);
104 case 0x11:
105 return s->ter;
106 default:
107 return 0;
108 }
109 }
110
111 static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
112 {
113 switch (addr) {
114 case 0:
115 if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
116 m5206_timer_reset(s);
117 }
118 s->tmr = val;
119 m5206_timer_recalibrate(s);
120 break;
121 case 4:
122 s->trr = val;
123 m5206_timer_recalibrate(s);
124 break;
125 case 8:
126 s->tcr = val;
127 break;
128 case 0xc:
129 ptimer_transaction_begin(s->timer);
130 ptimer_set_count(s->timer, val);
131 ptimer_transaction_commit(s->timer);
132 break;
133 case 0x11:
134 s->ter &= ~val;
135 break;
136 default:
137 break;
138 }
139 m5206_timer_update(s);
140 }
141
142 static m5206_timer_state *m5206_timer_init(qemu_irq irq)
143 {
144 m5206_timer_state *s;
145
146 s = g_new0(m5206_timer_state, 1);
147 s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_DEFAULT);
148 s->irq = irq;
149 m5206_timer_reset(s);
150 return s;
151 }
152
153 /* System Integration Module. */
154
155 typedef struct {
156 M68kCPU *cpu;
157 MemoryRegion iomem;
158 m5206_timer_state *timer[2];
159 void *uart[2];
160 uint8_t scr;
161 uint8_t icr[14];
162 uint16_t imr; /* 1 == interrupt is masked. */
163 uint16_t ipr;
164 uint8_t rsr;
165 uint8_t swivr;
166 uint8_t par;
167 /* Include the UART vector registers here. */
168 uint8_t uivr[2];
169 } m5206_mbar_state;
170
171 /* Interrupt controller. */
172
173 static int m5206_find_pending_irq(m5206_mbar_state *s)
174 {
175 int level;
176 int vector;
177 uint16_t active;
178 int i;
179
180 level = 0;
181 vector = 0;
182 active = s->ipr & ~s->imr;
183 if (!active)
184 return 0;
185
186 for (i = 1; i < 14; i++) {
187 if (active & (1 << i)) {
188 if ((s->icr[i] & 0x1f) > level) {
189 level = s->icr[i] & 0x1f;
190 vector = i;
191 }
192 }
193 }
194
195 if (level < 4)
196 vector = 0;
197
198 return vector;
199 }
200
201 static void m5206_mbar_update(m5206_mbar_state *s)
202 {
203 int irq;
204 int vector;
205 int level;
206
207 irq = m5206_find_pending_irq(s);
208 if (irq) {
209 int tmp;
210 tmp = s->icr[irq];
211 level = (tmp >> 2) & 7;
212 if (tmp & 0x80) {
213 /* Autovector. */
214 vector = 24 + level;
215 } else {
216 switch (irq) {
217 case 8: /* SWT */
218 vector = s->swivr;
219 break;
220 case 12: /* UART1 */
221 vector = s->uivr[0];
222 break;
223 case 13: /* UART2 */
224 vector = s->uivr[1];
225 break;
226 default:
227 /* Unknown vector. */
228 error_report("Unhandled vector for IRQ %d", irq);
229 vector = 0xf;
230 break;
231 }
232 }
233 } else {
234 level = 0;
235 vector = 0;
236 }
237 m68k_set_irq_level(s->cpu, level, vector);
238 }
239
240 static void m5206_mbar_set_irq(void *opaque, int irq, int level)
241 {
242 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
243 if (level) {
244 s->ipr |= 1 << irq;
245 } else {
246 s->ipr &= ~(1 << irq);
247 }
248 m5206_mbar_update(s);
249 }
250
251 /* System Integration Module. */
252
253 static void m5206_mbar_reset(m5206_mbar_state *s)
254 {
255 s->scr = 0xc0;
256 s->icr[1] = 0x04;
257 s->icr[2] = 0x08;
258 s->icr[3] = 0x0c;
259 s->icr[4] = 0x10;
260 s->icr[5] = 0x14;
261 s->icr[6] = 0x18;
262 s->icr[7] = 0x1c;
263 s->icr[8] = 0x1c;
264 s->icr[9] = 0x80;
265 s->icr[10] = 0x80;
266 s->icr[11] = 0x80;
267 s->icr[12] = 0x00;
268 s->icr[13] = 0x00;
269 s->imr = 0x3ffe;
270 s->rsr = 0x80;
271 s->swivr = 0x0f;
272 s->par = 0;
273 }
274
275 static uint64_t m5206_mbar_read(m5206_mbar_state *s,
276 uint64_t offset, unsigned size)
277 {
278 if (offset >= 0x100 && offset < 0x120) {
279 return m5206_timer_read(s->timer[0], offset - 0x100);
280 } else if (offset >= 0x120 && offset < 0x140) {
281 return m5206_timer_read(s->timer[1], offset - 0x120);
282 } else if (offset >= 0x140 && offset < 0x160) {
283 return mcf_uart_read(s->uart[0], offset - 0x140, size);
284 } else if (offset >= 0x180 && offset < 0x1a0) {
285 return mcf_uart_read(s->uart[1], offset - 0x180, size);
286 }
287 switch (offset) {
288 case 0x03: return s->scr;
289 case 0x14 ... 0x20: return s->icr[offset - 0x13];
290 case 0x36: return s->imr;
291 case 0x3a: return s->ipr;
292 case 0x40: return s->rsr;
293 case 0x41: return 0;
294 case 0x42: return s->swivr;
295 case 0x50:
296 /* DRAM mask register. */
297 /* FIXME: currently hardcoded to 128Mb. */
298 {
299 uint32_t mask = ~0;
300 while (mask > ram_size)
301 mask >>= 1;
302 return mask & 0x0ffe0000;
303 }
304 case 0x5c: return 1; /* DRAM bank 1 empty. */
305 case 0xcb: return s->par;
306 case 0x170: return s->uivr[0];
307 case 0x1b0: return s->uivr[1];
308 }
309 hw_error("Bad MBAR read offset 0x%x", (int)offset);
310 return 0;
311 }
312
313 static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset,
314 uint64_t value, unsigned size)
315 {
316 if (offset >= 0x100 && offset < 0x120) {
317 m5206_timer_write(s->timer[0], offset - 0x100, value);
318 return;
319 } else if (offset >= 0x120 && offset < 0x140) {
320 m5206_timer_write(s->timer[1], offset - 0x120, value);
321 return;
322 } else if (offset >= 0x140 && offset < 0x160) {
323 mcf_uart_write(s->uart[0], offset - 0x140, value, size);
324 return;
325 } else if (offset >= 0x180 && offset < 0x1a0) {
326 mcf_uart_write(s->uart[1], offset - 0x180, value, size);
327 return;
328 }
329 switch (offset) {
330 case 0x03:
331 s->scr = value;
332 break;
333 case 0x14 ... 0x20:
334 s->icr[offset - 0x13] = value;
335 m5206_mbar_update(s);
336 break;
337 case 0x36:
338 s->imr = value;
339 m5206_mbar_update(s);
340 break;
341 case 0x40:
342 s->rsr &= ~value;
343 break;
344 case 0x41:
345 /* TODO: implement watchdog. */
346 break;
347 case 0x42:
348 s->swivr = value;
349 break;
350 case 0xcb:
351 s->par = value;
352 break;
353 case 0x170:
354 s->uivr[0] = value;
355 break;
356 case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
357 /* Not implemented: UART Output port bits. */
358 break;
359 case 0x1b0:
360 s->uivr[1] = value;
361 break;
362 default:
363 hw_error("Bad MBAR write offset 0x%x", (int)offset);
364 break;
365 }
366 }
367
368 /* Internal peripherals use a variety of register widths.
369 This lookup table allows a single routine to handle all of them. */
370 static const uint8_t m5206_mbar_width[] =
371 {
372 /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
373 /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2,
374 /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4,
375 /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376 /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0,
377 /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
378 /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
379 /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
380 };
381
382 static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset);
383 static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset);
384
385 static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset)
386 {
387 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
388 offset &= 0x3ff;
389 if (offset >= 0x200) {
390 hw_error("Bad MBAR read offset 0x%x", (int)offset);
391 }
392 if (m5206_mbar_width[offset >> 2] > 1) {
393 uint16_t val;
394 val = m5206_mbar_readw(opaque, offset & ~1);
395 if ((offset & 1) == 0) {
396 val >>= 8;
397 }
398 return val & 0xff;
399 }
400 return m5206_mbar_read(s, offset, 1);
401 }
402
403 static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset)
404 {
405 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
406 int width;
407 offset &= 0x3ff;
408 if (offset >= 0x200) {
409 hw_error("Bad MBAR read offset 0x%x", (int)offset);
410 }
411 width = m5206_mbar_width[offset >> 2];
412 if (width > 2) {
413 uint32_t val;
414 val = m5206_mbar_readl(opaque, offset & ~3);
415 if ((offset & 3) == 0)
416 val >>= 16;
417 return val & 0xffff;
418 } else if (width < 2) {
419 uint16_t val;
420 val = m5206_mbar_readb(opaque, offset) << 8;
421 val |= m5206_mbar_readb(opaque, offset + 1);
422 return val;
423 }
424 return m5206_mbar_read(s, offset, 2);
425 }
426
427 static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset)
428 {
429 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
430 int width;
431 offset &= 0x3ff;
432 if (offset >= 0x200) {
433 hw_error("Bad MBAR read offset 0x%x", (int)offset);
434 }
435 width = m5206_mbar_width[offset >> 2];
436 if (width < 4) {
437 uint32_t val;
438 val = m5206_mbar_readw(opaque, offset) << 16;
439 val |= m5206_mbar_readw(opaque, offset + 2);
440 return val;
441 }
442 return m5206_mbar_read(s, offset, 4);
443 }
444
445 static void m5206_mbar_writew(void *opaque, hwaddr offset,
446 uint32_t value);
447 static void m5206_mbar_writel(void *opaque, hwaddr offset,
448 uint32_t value);
449
450 static void m5206_mbar_writeb(void *opaque, hwaddr offset,
451 uint32_t value)
452 {
453 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
454 int width;
455 offset &= 0x3ff;
456 if (offset >= 0x200) {
457 hw_error("Bad MBAR write offset 0x%x", (int)offset);
458 }
459 width = m5206_mbar_width[offset >> 2];
460 if (width > 1) {
461 uint32_t tmp;
462 tmp = m5206_mbar_readw(opaque, offset & ~1);
463 if (offset & 1) {
464 tmp = (tmp & 0xff00) | value;
465 } else {
466 tmp = (tmp & 0x00ff) | (value << 8);
467 }
468 m5206_mbar_writew(opaque, offset & ~1, tmp);
469 return;
470 }
471 m5206_mbar_write(s, offset, value, 1);
472 }
473
474 static void m5206_mbar_writew(void *opaque, hwaddr offset,
475 uint32_t value)
476 {
477 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
478 int width;
479 offset &= 0x3ff;
480 if (offset >= 0x200) {
481 hw_error("Bad MBAR write offset 0x%x", (int)offset);
482 }
483 width = m5206_mbar_width[offset >> 2];
484 if (width > 2) {
485 uint32_t tmp;
486 tmp = m5206_mbar_readl(opaque, offset & ~3);
487 if (offset & 3) {
488 tmp = (tmp & 0xffff0000) | value;
489 } else {
490 tmp = (tmp & 0x0000ffff) | (value << 16);
491 }
492 m5206_mbar_writel(opaque, offset & ~3, tmp);
493 return;
494 } else if (width < 2) {
495 m5206_mbar_writeb(opaque, offset, value >> 8);
496 m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
497 return;
498 }
499 m5206_mbar_write(s, offset, value, 2);
500 }
501
502 static void m5206_mbar_writel(void *opaque, hwaddr offset,
503 uint32_t value)
504 {
505 m5206_mbar_state *s = (m5206_mbar_state *)opaque;
506 int width;
507 offset &= 0x3ff;
508 if (offset >= 0x200) {
509 hw_error("Bad MBAR write offset 0x%x", (int)offset);
510 }
511 width = m5206_mbar_width[offset >> 2];
512 if (width < 4) {
513 m5206_mbar_writew(opaque, offset, value >> 16);
514 m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
515 return;
516 }
517 m5206_mbar_write(s, offset, value, 4);
518 }
519
520 static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size)
521 {
522 switch (size) {
523 case 1:
524 return m5206_mbar_readb(opaque, addr);
525 case 2:
526 return m5206_mbar_readw(opaque, addr);
527 case 4:
528 return m5206_mbar_readl(opaque, addr);
529 default:
530 g_assert_not_reached();
531 }
532 }
533
534 static void m5206_mbar_writefn(void *opaque, hwaddr addr,
535 uint64_t value, unsigned size)
536 {
537 switch (size) {
538 case 1:
539 m5206_mbar_writeb(opaque, addr, value);
540 break;
541 case 2:
542 m5206_mbar_writew(opaque, addr, value);
543 break;
544 case 4:
545 m5206_mbar_writel(opaque, addr, value);
546 break;
547 default:
548 g_assert_not_reached();
549 }
550 }
551
552 static const MemoryRegionOps m5206_mbar_ops = {
553 .read = m5206_mbar_readfn,
554 .write = m5206_mbar_writefn,
555 .valid.min_access_size = 1,
556 .valid.max_access_size = 4,
557 .endianness = DEVICE_NATIVE_ENDIAN,
558 };
559
560 qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu)
561 {
562 m5206_mbar_state *s;
563 qemu_irq *pic;
564
565 s = g_new0(m5206_mbar_state, 1);
566
567 memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s,
568 "mbar", 0x00001000);
569 memory_region_add_subregion(sysmem, base, &s->iomem);
570
571 pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
572 s->timer[0] = m5206_timer_init(pic[9]);
573 s->timer[1] = m5206_timer_init(pic[10]);
574 s->uart[0] = mcf_uart_init(pic[12], serial_hd(0));
575 s->uart[1] = mcf_uart_init(pic[13], serial_hd(1));
576 s->cpu = cpu;
577
578 m5206_mbar_reset(s);
579 return pic;
580 }