19743b581SPaolo Bonzini /*
29743b581SPaolo Bonzini * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
39743b581SPaolo Bonzini *
49743b581SPaolo Bonzini * Copyright (c) 2007 CodeSourcery.
59743b581SPaolo Bonzini *
69743b581SPaolo Bonzini * This code is licensed under the GPL
79743b581SPaolo Bonzini */
864552b6bSMarkus Armbruster
9d8416665SPeter Maydell #include "qemu/osdep.h"
1045876e91SAlistair Francis #include "qemu/error-report.h"
11b8096678SPhilippe Mathieu-Daudé #include "qemu/log.h"
124771d756SPaolo Bonzini #include "cpu.h"
13989c8a46SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
145601d241SPaolo Bonzini #include "hw/boards.h"
1564552b6bSMarkus Armbruster #include "hw/irq.h"
160d09e41aSPaolo Bonzini #include "hw/m68k/mcf.h"
179743b581SPaolo Bonzini #include "qemu/timer.h"
189743b581SPaolo Bonzini #include "hw/ptimer.h"
199743b581SPaolo Bonzini #include "sysemu/sysemu.h"
200bc6746eSThomas Huth #include "hw/sysbus.h"
219743b581SPaolo Bonzini
229743b581SPaolo Bonzini /* General purpose timer module. */
239743b581SPaolo Bonzini typedef struct {
249743b581SPaolo Bonzini uint16_t tmr;
259743b581SPaolo Bonzini uint16_t trr;
269743b581SPaolo Bonzini uint16_t tcr;
279743b581SPaolo Bonzini uint16_t ter;
289743b581SPaolo Bonzini ptimer_state *timer;
299743b581SPaolo Bonzini qemu_irq irq;
309743b581SPaolo Bonzini int irq_state;
319743b581SPaolo Bonzini } m5206_timer_state;
329743b581SPaolo Bonzini
339743b581SPaolo Bonzini #define TMR_RST 0x01
349743b581SPaolo Bonzini #define TMR_CLK 0x06
359743b581SPaolo Bonzini #define TMR_FRR 0x08
369743b581SPaolo Bonzini #define TMR_ORI 0x10
379743b581SPaolo Bonzini #define TMR_OM 0x20
389743b581SPaolo Bonzini #define TMR_CE 0xc0
399743b581SPaolo Bonzini
409743b581SPaolo Bonzini #define TER_CAP 0x01
419743b581SPaolo Bonzini #define TER_REF 0x02
429743b581SPaolo Bonzini
m5206_timer_update(m5206_timer_state * s)439743b581SPaolo Bonzini static void m5206_timer_update(m5206_timer_state *s)
449743b581SPaolo Bonzini {
459743b581SPaolo Bonzini if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
469743b581SPaolo Bonzini qemu_irq_raise(s->irq);
479743b581SPaolo Bonzini else
489743b581SPaolo Bonzini qemu_irq_lower(s->irq);
499743b581SPaolo Bonzini }
509743b581SPaolo Bonzini
m5206_timer_reset(m5206_timer_state * s)519743b581SPaolo Bonzini static void m5206_timer_reset(m5206_timer_state *s)
529743b581SPaolo Bonzini {
539743b581SPaolo Bonzini s->tmr = 0;
549743b581SPaolo Bonzini s->trr = 0;
559743b581SPaolo Bonzini }
569743b581SPaolo Bonzini
m5206_timer_recalibrate(m5206_timer_state * s)579743b581SPaolo Bonzini static void m5206_timer_recalibrate(m5206_timer_state *s)
589743b581SPaolo Bonzini {
599743b581SPaolo Bonzini int prescale;
609743b581SPaolo Bonzini int mode;
619743b581SPaolo Bonzini
62efe62d6fSPeter Maydell ptimer_transaction_begin(s->timer);
639743b581SPaolo Bonzini ptimer_stop(s->timer);
649743b581SPaolo Bonzini
65efe62d6fSPeter Maydell if ((s->tmr & TMR_RST) == 0) {
66efe62d6fSPeter Maydell goto exit;
67efe62d6fSPeter Maydell }
689743b581SPaolo Bonzini
699743b581SPaolo Bonzini prescale = (s->tmr >> 8) + 1;
709743b581SPaolo Bonzini mode = (s->tmr >> 1) & 3;
719743b581SPaolo Bonzini if (mode == 2)
729743b581SPaolo Bonzini prescale *= 16;
739743b581SPaolo Bonzini
74c7aab58bSThomas Huth if (mode == 3 || mode == 0) {
75c7aab58bSThomas Huth qemu_log_mask(LOG_UNIMP, "m5206_timer: mode %d not implemented\n",
76c7aab58bSThomas Huth mode);
77c7aab58bSThomas Huth goto exit;
78c7aab58bSThomas Huth }
79c7aab58bSThomas Huth if ((s->tmr & TMR_FRR) == 0) {
80c7aab58bSThomas Huth qemu_log_mask(LOG_UNIMP,
81c7aab58bSThomas Huth "m5206_timer: free running mode not implemented\n");
82c7aab58bSThomas Huth goto exit;
83c7aab58bSThomas Huth }
849743b581SPaolo Bonzini
859743b581SPaolo Bonzini /* Assume 66MHz system clock. */
869743b581SPaolo Bonzini ptimer_set_freq(s->timer, 66000000 / prescale);
879743b581SPaolo Bonzini
889743b581SPaolo Bonzini ptimer_set_limit(s->timer, s->trr, 0);
899743b581SPaolo Bonzini
909743b581SPaolo Bonzini ptimer_run(s->timer, 0);
91efe62d6fSPeter Maydell exit:
92efe62d6fSPeter Maydell ptimer_transaction_commit(s->timer);
939743b581SPaolo Bonzini }
949743b581SPaolo Bonzini
m5206_timer_trigger(void * opaque)959743b581SPaolo Bonzini static void m5206_timer_trigger(void *opaque)
969743b581SPaolo Bonzini {
979743b581SPaolo Bonzini m5206_timer_state *s = (m5206_timer_state *)opaque;
989743b581SPaolo Bonzini s->ter |= TER_REF;
999743b581SPaolo Bonzini m5206_timer_update(s);
1009743b581SPaolo Bonzini }
1019743b581SPaolo Bonzini
m5206_timer_read(m5206_timer_state * s,uint32_t addr)1029743b581SPaolo Bonzini static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
1039743b581SPaolo Bonzini {
1049743b581SPaolo Bonzini switch (addr) {
1059743b581SPaolo Bonzini case 0:
1069743b581SPaolo Bonzini return s->tmr;
1079743b581SPaolo Bonzini case 4:
1089743b581SPaolo Bonzini return s->trr;
1099743b581SPaolo Bonzini case 8:
1109743b581SPaolo Bonzini return s->tcr;
1119743b581SPaolo Bonzini case 0xc:
1129743b581SPaolo Bonzini return s->trr - ptimer_get_count(s->timer);
1139743b581SPaolo Bonzini case 0x11:
1149743b581SPaolo Bonzini return s->ter;
1159743b581SPaolo Bonzini default:
1169743b581SPaolo Bonzini return 0;
1179743b581SPaolo Bonzini }
1189743b581SPaolo Bonzini }
1199743b581SPaolo Bonzini
m5206_timer_write(m5206_timer_state * s,uint32_t addr,uint32_t val)1209743b581SPaolo Bonzini static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
1219743b581SPaolo Bonzini {
1229743b581SPaolo Bonzini switch (addr) {
1239743b581SPaolo Bonzini case 0:
1249743b581SPaolo Bonzini if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
1259743b581SPaolo Bonzini m5206_timer_reset(s);
1269743b581SPaolo Bonzini }
1279743b581SPaolo Bonzini s->tmr = val;
1289743b581SPaolo Bonzini m5206_timer_recalibrate(s);
1299743b581SPaolo Bonzini break;
1309743b581SPaolo Bonzini case 4:
1319743b581SPaolo Bonzini s->trr = val;
1329743b581SPaolo Bonzini m5206_timer_recalibrate(s);
1339743b581SPaolo Bonzini break;
1349743b581SPaolo Bonzini case 8:
1359743b581SPaolo Bonzini s->tcr = val;
1369743b581SPaolo Bonzini break;
1379743b581SPaolo Bonzini case 0xc:
138efe62d6fSPeter Maydell ptimer_transaction_begin(s->timer);
1399743b581SPaolo Bonzini ptimer_set_count(s->timer, val);
140efe62d6fSPeter Maydell ptimer_transaction_commit(s->timer);
1419743b581SPaolo Bonzini break;
1429743b581SPaolo Bonzini case 0x11:
1439743b581SPaolo Bonzini s->ter &= ~val;
1449743b581SPaolo Bonzini break;
1459743b581SPaolo Bonzini default:
1469743b581SPaolo Bonzini break;
1479743b581SPaolo Bonzini }
1489743b581SPaolo Bonzini m5206_timer_update(s);
1499743b581SPaolo Bonzini }
1509743b581SPaolo Bonzini
m5206_timer_init(m5206_timer_state * s,qemu_irq irq)1515e077a77SThomas Huth static void m5206_timer_init(m5206_timer_state *s, qemu_irq irq)
1529743b581SPaolo Bonzini {
1539598c1bbSPeter Maydell s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_LEGACY);
1549743b581SPaolo Bonzini s->irq = irq;
1559743b581SPaolo Bonzini m5206_timer_reset(s);
1569743b581SPaolo Bonzini }
1579743b581SPaolo Bonzini
1589743b581SPaolo Bonzini /* System Integration Module. */
1599743b581SPaolo Bonzini
1609743b581SPaolo Bonzini typedef struct {
1610bc6746eSThomas Huth SysBusDevice parent_obj;
1620bc6746eSThomas Huth
1639743b581SPaolo Bonzini M68kCPU *cpu;
1649743b581SPaolo Bonzini MemoryRegion iomem;
165b0bed2c9SPeter Maydell qemu_irq *pic;
1665e077a77SThomas Huth m5206_timer_state timer[2];
167f213ccc9SPhilippe Mathieu-Daudé DeviceState *uart[2];
1689743b581SPaolo Bonzini uint8_t scr;
1699743b581SPaolo Bonzini uint8_t icr[14];
1709743b581SPaolo Bonzini uint16_t imr; /* 1 == interrupt is masked. */
1719743b581SPaolo Bonzini uint16_t ipr;
1729743b581SPaolo Bonzini uint8_t rsr;
1739743b581SPaolo Bonzini uint8_t swivr;
1749743b581SPaolo Bonzini uint8_t par;
1759743b581SPaolo Bonzini /* Include the UART vector registers here. */
1769743b581SPaolo Bonzini uint8_t uivr[2];
1779743b581SPaolo Bonzini } m5206_mbar_state;
1789743b581SPaolo Bonzini
1790bc6746eSThomas Huth #define MCF5206_MBAR(obj) OBJECT_CHECK(m5206_mbar_state, (obj), TYPE_MCF5206_MBAR)
1800bc6746eSThomas Huth
1819743b581SPaolo Bonzini /* Interrupt controller. */
1829743b581SPaolo Bonzini
m5206_find_pending_irq(m5206_mbar_state * s)1839743b581SPaolo Bonzini static int m5206_find_pending_irq(m5206_mbar_state *s)
1849743b581SPaolo Bonzini {
1859743b581SPaolo Bonzini int level;
1869743b581SPaolo Bonzini int vector;
1879743b581SPaolo Bonzini uint16_t active;
1889743b581SPaolo Bonzini int i;
1899743b581SPaolo Bonzini
1909743b581SPaolo Bonzini level = 0;
1919743b581SPaolo Bonzini vector = 0;
1929743b581SPaolo Bonzini active = s->ipr & ~s->imr;
1939743b581SPaolo Bonzini if (!active)
1949743b581SPaolo Bonzini return 0;
1959743b581SPaolo Bonzini
1969743b581SPaolo Bonzini for (i = 1; i < 14; i++) {
1979743b581SPaolo Bonzini if (active & (1 << i)) {
1989743b581SPaolo Bonzini if ((s->icr[i] & 0x1f) > level) {
1999743b581SPaolo Bonzini level = s->icr[i] & 0x1f;
2009743b581SPaolo Bonzini vector = i;
2019743b581SPaolo Bonzini }
2029743b581SPaolo Bonzini }
2039743b581SPaolo Bonzini }
2049743b581SPaolo Bonzini
2059743b581SPaolo Bonzini if (level < 4)
2069743b581SPaolo Bonzini vector = 0;
2079743b581SPaolo Bonzini
2089743b581SPaolo Bonzini return vector;
2099743b581SPaolo Bonzini }
2109743b581SPaolo Bonzini
m5206_mbar_update(m5206_mbar_state * s)2119743b581SPaolo Bonzini static void m5206_mbar_update(m5206_mbar_state *s)
2129743b581SPaolo Bonzini {
2139743b581SPaolo Bonzini int irq;
2149743b581SPaolo Bonzini int vector;
2159743b581SPaolo Bonzini int level;
2169743b581SPaolo Bonzini
2179743b581SPaolo Bonzini irq = m5206_find_pending_irq(s);
2189743b581SPaolo Bonzini if (irq) {
2199743b581SPaolo Bonzini int tmp;
2209743b581SPaolo Bonzini tmp = s->icr[irq];
2219743b581SPaolo Bonzini level = (tmp >> 2) & 7;
2229743b581SPaolo Bonzini if (tmp & 0x80) {
2239743b581SPaolo Bonzini /* Autovector. */
2249743b581SPaolo Bonzini vector = 24 + level;
2259743b581SPaolo Bonzini } else {
2269743b581SPaolo Bonzini switch (irq) {
2279743b581SPaolo Bonzini case 8: /* SWT */
2289743b581SPaolo Bonzini vector = s->swivr;
2299743b581SPaolo Bonzini break;
2309743b581SPaolo Bonzini case 12: /* UART1 */
2319743b581SPaolo Bonzini vector = s->uivr[0];
2329743b581SPaolo Bonzini break;
2339743b581SPaolo Bonzini case 13: /* UART2 */
2349743b581SPaolo Bonzini vector = s->uivr[1];
2359743b581SPaolo Bonzini break;
2369743b581SPaolo Bonzini default:
2379743b581SPaolo Bonzini /* Unknown vector. */
238b8096678SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP, "%s: Unhandled vector for IRQ %d\n",
239b8096678SPhilippe Mathieu-Daudé __func__, irq);
2409743b581SPaolo Bonzini vector = 0xf;
2419743b581SPaolo Bonzini break;
2429743b581SPaolo Bonzini }
2439743b581SPaolo Bonzini }
2449743b581SPaolo Bonzini } else {
2459743b581SPaolo Bonzini level = 0;
2469743b581SPaolo Bonzini vector = 0;
2479743b581SPaolo Bonzini }
2489743b581SPaolo Bonzini m68k_set_irq_level(s->cpu, level, vector);
2499743b581SPaolo Bonzini }
2509743b581SPaolo Bonzini
m5206_mbar_set_irq(void * opaque,int irq,int level)2519743b581SPaolo Bonzini static void m5206_mbar_set_irq(void *opaque, int irq, int level)
2529743b581SPaolo Bonzini {
2539743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
2549743b581SPaolo Bonzini if (level) {
2559743b581SPaolo Bonzini s->ipr |= 1 << irq;
2569743b581SPaolo Bonzini } else {
2579743b581SPaolo Bonzini s->ipr &= ~(1 << irq);
2589743b581SPaolo Bonzini }
2599743b581SPaolo Bonzini m5206_mbar_update(s);
2609743b581SPaolo Bonzini }
2619743b581SPaolo Bonzini
2629743b581SPaolo Bonzini /* System Integration Module. */
2639743b581SPaolo Bonzini
m5206_mbar_reset(DeviceState * dev)2640bc6746eSThomas Huth static void m5206_mbar_reset(DeviceState *dev)
2659743b581SPaolo Bonzini {
2660bc6746eSThomas Huth m5206_mbar_state *s = MCF5206_MBAR(dev);
2670bc6746eSThomas Huth
2689743b581SPaolo Bonzini s->scr = 0xc0;
2699743b581SPaolo Bonzini s->icr[1] = 0x04;
2709743b581SPaolo Bonzini s->icr[2] = 0x08;
2719743b581SPaolo Bonzini s->icr[3] = 0x0c;
2729743b581SPaolo Bonzini s->icr[4] = 0x10;
2739743b581SPaolo Bonzini s->icr[5] = 0x14;
2749743b581SPaolo Bonzini s->icr[6] = 0x18;
2759743b581SPaolo Bonzini s->icr[7] = 0x1c;
2769743b581SPaolo Bonzini s->icr[8] = 0x1c;
2779743b581SPaolo Bonzini s->icr[9] = 0x80;
2789743b581SPaolo Bonzini s->icr[10] = 0x80;
2799743b581SPaolo Bonzini s->icr[11] = 0x80;
2809743b581SPaolo Bonzini s->icr[12] = 0x00;
2819743b581SPaolo Bonzini s->icr[13] = 0x00;
2829743b581SPaolo Bonzini s->imr = 0x3ffe;
2839743b581SPaolo Bonzini s->rsr = 0x80;
2849743b581SPaolo Bonzini s->swivr = 0x0f;
2859743b581SPaolo Bonzini s->par = 0;
2869743b581SPaolo Bonzini }
2879743b581SPaolo Bonzini
m5206_mbar_read(m5206_mbar_state * s,uint16_t offset,unsigned size)2889743b581SPaolo Bonzini static uint64_t m5206_mbar_read(m5206_mbar_state *s,
289ccff1ae4SPhilippe Mathieu-Daudé uint16_t offset, unsigned size)
2909743b581SPaolo Bonzini {
2919743b581SPaolo Bonzini if (offset >= 0x100 && offset < 0x120) {
2925e077a77SThomas Huth return m5206_timer_read(&s->timer[0], offset - 0x100);
2939743b581SPaolo Bonzini } else if (offset >= 0x120 && offset < 0x140) {
2945e077a77SThomas Huth return m5206_timer_read(&s->timer[1], offset - 0x120);
2959743b581SPaolo Bonzini } else if (offset >= 0x140 && offset < 0x160) {
2969743b581SPaolo Bonzini return mcf_uart_read(s->uart[0], offset - 0x140, size);
2979743b581SPaolo Bonzini } else if (offset >= 0x180 && offset < 0x1a0) {
2989743b581SPaolo Bonzini return mcf_uart_read(s->uart[1], offset - 0x180, size);
2999743b581SPaolo Bonzini }
3009743b581SPaolo Bonzini switch (offset) {
3019743b581SPaolo Bonzini case 0x03: return s->scr;
3029743b581SPaolo Bonzini case 0x14 ... 0x20: return s->icr[offset - 0x13];
3039743b581SPaolo Bonzini case 0x36: return s->imr;
3049743b581SPaolo Bonzini case 0x3a: return s->ipr;
3059743b581SPaolo Bonzini case 0x40: return s->rsr;
3069743b581SPaolo Bonzini case 0x41: return 0;
3079743b581SPaolo Bonzini case 0x42: return s->swivr;
3089743b581SPaolo Bonzini case 0x50:
3099743b581SPaolo Bonzini /* DRAM mask register. */
3109743b581SPaolo Bonzini /* FIXME: currently hardcoded to 128Mb. */
3119743b581SPaolo Bonzini {
3129743b581SPaolo Bonzini uint32_t mask = ~0;
3135601d241SPaolo Bonzini while (mask > current_machine->ram_size) {
3149743b581SPaolo Bonzini mask >>= 1;
3155601d241SPaolo Bonzini }
3169743b581SPaolo Bonzini return mask & 0x0ffe0000;
3179743b581SPaolo Bonzini }
3189743b581SPaolo Bonzini case 0x5c: return 1; /* DRAM bank 1 empty. */
3199743b581SPaolo Bonzini case 0xcb: return s->par;
3209743b581SPaolo Bonzini case 0x170: return s->uivr[0];
3219743b581SPaolo Bonzini case 0x1b0: return s->uivr[1];
3229743b581SPaolo Bonzini }
323b8096678SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n",
324b8096678SPhilippe Mathieu-Daudé __func__, offset);
3259743b581SPaolo Bonzini return 0;
3269743b581SPaolo Bonzini }
3279743b581SPaolo Bonzini
m5206_mbar_write(m5206_mbar_state * s,uint16_t offset,uint64_t value,unsigned size)328ccff1ae4SPhilippe Mathieu-Daudé static void m5206_mbar_write(m5206_mbar_state *s, uint16_t offset,
3299743b581SPaolo Bonzini uint64_t value, unsigned size)
3309743b581SPaolo Bonzini {
3319743b581SPaolo Bonzini if (offset >= 0x100 && offset < 0x120) {
3325e077a77SThomas Huth m5206_timer_write(&s->timer[0], offset - 0x100, value);
3339743b581SPaolo Bonzini return;
3349743b581SPaolo Bonzini } else if (offset >= 0x120 && offset < 0x140) {
3355e077a77SThomas Huth m5206_timer_write(&s->timer[1], offset - 0x120, value);
3369743b581SPaolo Bonzini return;
3379743b581SPaolo Bonzini } else if (offset >= 0x140 && offset < 0x160) {
3389743b581SPaolo Bonzini mcf_uart_write(s->uart[0], offset - 0x140, value, size);
3399743b581SPaolo Bonzini return;
3409743b581SPaolo Bonzini } else if (offset >= 0x180 && offset < 0x1a0) {
3419743b581SPaolo Bonzini mcf_uart_write(s->uart[1], offset - 0x180, value, size);
3429743b581SPaolo Bonzini return;
3439743b581SPaolo Bonzini }
3449743b581SPaolo Bonzini switch (offset) {
3459743b581SPaolo Bonzini case 0x03:
3469743b581SPaolo Bonzini s->scr = value;
3479743b581SPaolo Bonzini break;
3489743b581SPaolo Bonzini case 0x14 ... 0x20:
3499743b581SPaolo Bonzini s->icr[offset - 0x13] = value;
3509743b581SPaolo Bonzini m5206_mbar_update(s);
3519743b581SPaolo Bonzini break;
3529743b581SPaolo Bonzini case 0x36:
3539743b581SPaolo Bonzini s->imr = value;
3549743b581SPaolo Bonzini m5206_mbar_update(s);
3559743b581SPaolo Bonzini break;
3569743b581SPaolo Bonzini case 0x40:
3579743b581SPaolo Bonzini s->rsr &= ~value;
3589743b581SPaolo Bonzini break;
3599743b581SPaolo Bonzini case 0x41:
3609743b581SPaolo Bonzini /* TODO: implement watchdog. */
3619743b581SPaolo Bonzini break;
3629743b581SPaolo Bonzini case 0x42:
3639743b581SPaolo Bonzini s->swivr = value;
3649743b581SPaolo Bonzini break;
3659743b581SPaolo Bonzini case 0xcb:
3669743b581SPaolo Bonzini s->par = value;
3679743b581SPaolo Bonzini break;
3689743b581SPaolo Bonzini case 0x170:
3699743b581SPaolo Bonzini s->uivr[0] = value;
3709743b581SPaolo Bonzini break;
3719743b581SPaolo Bonzini case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
3729743b581SPaolo Bonzini /* Not implemented: UART Output port bits. */
3739743b581SPaolo Bonzini break;
3749743b581SPaolo Bonzini case 0x1b0:
3759743b581SPaolo Bonzini s->uivr[1] = value;
3769743b581SPaolo Bonzini break;
3779743b581SPaolo Bonzini default:
378b8096678SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n",
379b8096678SPhilippe Mathieu-Daudé __func__, offset);
3809743b581SPaolo Bonzini break;
3819743b581SPaolo Bonzini }
3829743b581SPaolo Bonzini }
3839743b581SPaolo Bonzini
3849743b581SPaolo Bonzini /* Internal peripherals use a variety of register widths.
3859743b581SPaolo Bonzini This lookup table allows a single routine to handle all of them. */
3869743b581SPaolo Bonzini static const uint8_t m5206_mbar_width[] =
3879743b581SPaolo Bonzini {
3889743b581SPaolo Bonzini /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
3899743b581SPaolo Bonzini /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2,
3909743b581SPaolo Bonzini /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4,
3919743b581SPaolo Bonzini /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3929743b581SPaolo Bonzini /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0,
3939743b581SPaolo Bonzini /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3949743b581SPaolo Bonzini /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3959743b581SPaolo Bonzini /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3969743b581SPaolo Bonzini };
3979743b581SPaolo Bonzini
3989743b581SPaolo Bonzini static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset);
3999743b581SPaolo Bonzini static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset);
4009743b581SPaolo Bonzini
m5206_mbar_readb(void * opaque,hwaddr offset)4019743b581SPaolo Bonzini static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset)
4029743b581SPaolo Bonzini {
4039743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
4049743b581SPaolo Bonzini offset &= 0x3ff;
4059743b581SPaolo Bonzini if (offset >= 0x200) {
406c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
407c7aab58bSThomas Huth offset);
408c7aab58bSThomas Huth return 0;
4099743b581SPaolo Bonzini }
4109743b581SPaolo Bonzini if (m5206_mbar_width[offset >> 2] > 1) {
4119743b581SPaolo Bonzini uint16_t val;
4129743b581SPaolo Bonzini val = m5206_mbar_readw(opaque, offset & ~1);
4139743b581SPaolo Bonzini if ((offset & 1) == 0) {
4149743b581SPaolo Bonzini val >>= 8;
4159743b581SPaolo Bonzini }
4169743b581SPaolo Bonzini return val & 0xff;
4179743b581SPaolo Bonzini }
4189743b581SPaolo Bonzini return m5206_mbar_read(s, offset, 1);
4199743b581SPaolo Bonzini }
4209743b581SPaolo Bonzini
m5206_mbar_readw(void * opaque,hwaddr offset)4219743b581SPaolo Bonzini static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset)
4229743b581SPaolo Bonzini {
4239743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
4249743b581SPaolo Bonzini int width;
4259743b581SPaolo Bonzini offset &= 0x3ff;
4269743b581SPaolo Bonzini if (offset >= 0x200) {
427c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
428c7aab58bSThomas Huth offset);
429c7aab58bSThomas Huth return 0;
4309743b581SPaolo Bonzini }
4319743b581SPaolo Bonzini width = m5206_mbar_width[offset >> 2];
4329743b581SPaolo Bonzini if (width > 2) {
4339743b581SPaolo Bonzini uint32_t val;
4349743b581SPaolo Bonzini val = m5206_mbar_readl(opaque, offset & ~3);
4359743b581SPaolo Bonzini if ((offset & 3) == 0)
4369743b581SPaolo Bonzini val >>= 16;
4379743b581SPaolo Bonzini return val & 0xffff;
4389743b581SPaolo Bonzini } else if (width < 2) {
4399743b581SPaolo Bonzini uint16_t val;
4409743b581SPaolo Bonzini val = m5206_mbar_readb(opaque, offset) << 8;
4419743b581SPaolo Bonzini val |= m5206_mbar_readb(opaque, offset + 1);
4429743b581SPaolo Bonzini return val;
4439743b581SPaolo Bonzini }
4449743b581SPaolo Bonzini return m5206_mbar_read(s, offset, 2);
4459743b581SPaolo Bonzini }
4469743b581SPaolo Bonzini
m5206_mbar_readl(void * opaque,hwaddr offset)4479743b581SPaolo Bonzini static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset)
4489743b581SPaolo Bonzini {
4499743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
4509743b581SPaolo Bonzini int width;
4519743b581SPaolo Bonzini offset &= 0x3ff;
4529743b581SPaolo Bonzini if (offset >= 0x200) {
453c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
454c7aab58bSThomas Huth offset);
455c7aab58bSThomas Huth return 0;
4569743b581SPaolo Bonzini }
4579743b581SPaolo Bonzini width = m5206_mbar_width[offset >> 2];
4589743b581SPaolo Bonzini if (width < 4) {
4599743b581SPaolo Bonzini uint32_t val;
4609743b581SPaolo Bonzini val = m5206_mbar_readw(opaque, offset) << 16;
4619743b581SPaolo Bonzini val |= m5206_mbar_readw(opaque, offset + 2);
4629743b581SPaolo Bonzini return val;
4639743b581SPaolo Bonzini }
4649743b581SPaolo Bonzini return m5206_mbar_read(s, offset, 4);
4659743b581SPaolo Bonzini }
4669743b581SPaolo Bonzini
4679743b581SPaolo Bonzini static void m5206_mbar_writew(void *opaque, hwaddr offset,
4689743b581SPaolo Bonzini uint32_t value);
4699743b581SPaolo Bonzini static void m5206_mbar_writel(void *opaque, hwaddr offset,
4709743b581SPaolo Bonzini uint32_t value);
4719743b581SPaolo Bonzini
m5206_mbar_writeb(void * opaque,hwaddr offset,uint32_t value)4729743b581SPaolo Bonzini static void m5206_mbar_writeb(void *opaque, hwaddr offset,
4739743b581SPaolo Bonzini uint32_t value)
4749743b581SPaolo Bonzini {
4759743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
4769743b581SPaolo Bonzini int width;
4779743b581SPaolo Bonzini offset &= 0x3ff;
4789743b581SPaolo Bonzini if (offset >= 0x200) {
479c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
480c7aab58bSThomas Huth offset);
481c7aab58bSThomas Huth return;
4829743b581SPaolo Bonzini }
4839743b581SPaolo Bonzini width = m5206_mbar_width[offset >> 2];
4849743b581SPaolo Bonzini if (width > 1) {
4859743b581SPaolo Bonzini uint32_t tmp;
4869743b581SPaolo Bonzini tmp = m5206_mbar_readw(opaque, offset & ~1);
4879743b581SPaolo Bonzini if (offset & 1) {
4889743b581SPaolo Bonzini tmp = (tmp & 0xff00) | value;
4899743b581SPaolo Bonzini } else {
4909743b581SPaolo Bonzini tmp = (tmp & 0x00ff) | (value << 8);
4919743b581SPaolo Bonzini }
4929743b581SPaolo Bonzini m5206_mbar_writew(opaque, offset & ~1, tmp);
4939743b581SPaolo Bonzini return;
4949743b581SPaolo Bonzini }
4959743b581SPaolo Bonzini m5206_mbar_write(s, offset, value, 1);
4969743b581SPaolo Bonzini }
4979743b581SPaolo Bonzini
m5206_mbar_writew(void * opaque,hwaddr offset,uint32_t value)4989743b581SPaolo Bonzini static void m5206_mbar_writew(void *opaque, hwaddr offset,
4999743b581SPaolo Bonzini uint32_t value)
5009743b581SPaolo Bonzini {
5019743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
5029743b581SPaolo Bonzini int width;
5039743b581SPaolo Bonzini offset &= 0x3ff;
5049743b581SPaolo Bonzini if (offset >= 0x200) {
505c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
506c7aab58bSThomas Huth offset);
507c7aab58bSThomas Huth return;
5089743b581SPaolo Bonzini }
5099743b581SPaolo Bonzini width = m5206_mbar_width[offset >> 2];
5109743b581SPaolo Bonzini if (width > 2) {
5119743b581SPaolo Bonzini uint32_t tmp;
5129743b581SPaolo Bonzini tmp = m5206_mbar_readl(opaque, offset & ~3);
5139743b581SPaolo Bonzini if (offset & 3) {
5149743b581SPaolo Bonzini tmp = (tmp & 0xffff0000) | value;
5159743b581SPaolo Bonzini } else {
5169743b581SPaolo Bonzini tmp = (tmp & 0x0000ffff) | (value << 16);
5179743b581SPaolo Bonzini }
5189743b581SPaolo Bonzini m5206_mbar_writel(opaque, offset & ~3, tmp);
5199743b581SPaolo Bonzini return;
5209743b581SPaolo Bonzini } else if (width < 2) {
5219743b581SPaolo Bonzini m5206_mbar_writeb(opaque, offset, value >> 8);
5229743b581SPaolo Bonzini m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
5239743b581SPaolo Bonzini return;
5249743b581SPaolo Bonzini }
5259743b581SPaolo Bonzini m5206_mbar_write(s, offset, value, 2);
5269743b581SPaolo Bonzini }
5279743b581SPaolo Bonzini
m5206_mbar_writel(void * opaque,hwaddr offset,uint32_t value)5289743b581SPaolo Bonzini static void m5206_mbar_writel(void *opaque, hwaddr offset,
5299743b581SPaolo Bonzini uint32_t value)
5309743b581SPaolo Bonzini {
5319743b581SPaolo Bonzini m5206_mbar_state *s = (m5206_mbar_state *)opaque;
5329743b581SPaolo Bonzini int width;
5339743b581SPaolo Bonzini offset &= 0x3ff;
5349743b581SPaolo Bonzini if (offset >= 0x200) {
535c7aab58bSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
536c7aab58bSThomas Huth offset);
537c7aab58bSThomas Huth return;
5389743b581SPaolo Bonzini }
5399743b581SPaolo Bonzini width = m5206_mbar_width[offset >> 2];
5409743b581SPaolo Bonzini if (width < 4) {
5419743b581SPaolo Bonzini m5206_mbar_writew(opaque, offset, value >> 16);
5429743b581SPaolo Bonzini m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
5439743b581SPaolo Bonzini return;
5449743b581SPaolo Bonzini }
5459743b581SPaolo Bonzini m5206_mbar_write(s, offset, value, 4);
5469743b581SPaolo Bonzini }
5479743b581SPaolo Bonzini
m5206_mbar_readfn(void * opaque,hwaddr addr,unsigned size)548bb428791SPeter Maydell static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size)
549bb428791SPeter Maydell {
550bb428791SPeter Maydell switch (size) {
551bb428791SPeter Maydell case 1:
552bb428791SPeter Maydell return m5206_mbar_readb(opaque, addr);
553bb428791SPeter Maydell case 2:
554bb428791SPeter Maydell return m5206_mbar_readw(opaque, addr);
555bb428791SPeter Maydell case 4:
556bb428791SPeter Maydell return m5206_mbar_readl(opaque, addr);
557bb428791SPeter Maydell default:
558bb428791SPeter Maydell g_assert_not_reached();
559bb428791SPeter Maydell }
560bb428791SPeter Maydell }
561bb428791SPeter Maydell
m5206_mbar_writefn(void * opaque,hwaddr addr,uint64_t value,unsigned size)562bb428791SPeter Maydell static void m5206_mbar_writefn(void *opaque, hwaddr addr,
563bb428791SPeter Maydell uint64_t value, unsigned size)
564bb428791SPeter Maydell {
565bb428791SPeter Maydell switch (size) {
566bb428791SPeter Maydell case 1:
567bb428791SPeter Maydell m5206_mbar_writeb(opaque, addr, value);
568bb428791SPeter Maydell break;
569bb428791SPeter Maydell case 2:
570bb428791SPeter Maydell m5206_mbar_writew(opaque, addr, value);
571bb428791SPeter Maydell break;
572bb428791SPeter Maydell case 4:
573bb428791SPeter Maydell m5206_mbar_writel(opaque, addr, value);
574bb428791SPeter Maydell break;
575bb428791SPeter Maydell default:
576bb428791SPeter Maydell g_assert_not_reached();
577bb428791SPeter Maydell }
578bb428791SPeter Maydell }
579bb428791SPeter Maydell
5809743b581SPaolo Bonzini static const MemoryRegionOps m5206_mbar_ops = {
581bb428791SPeter Maydell .read = m5206_mbar_readfn,
582bb428791SPeter Maydell .write = m5206_mbar_writefn,
583bb428791SPeter Maydell .valid.min_access_size = 1,
584bb428791SPeter Maydell .valid.max_access_size = 4,
5859743b581SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
5869743b581SPaolo Bonzini };
5879743b581SPaolo Bonzini
mcf5206_mbar_realize(DeviceState * dev,Error ** errp)5880bc6746eSThomas Huth static void mcf5206_mbar_realize(DeviceState *dev, Error **errp)
5899743b581SPaolo Bonzini {
5900bc6746eSThomas Huth m5206_mbar_state *s = MCF5206_MBAR(dev);
5919743b581SPaolo Bonzini
5922c9b15caSPaolo Bonzini memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s,
5939743b581SPaolo Bonzini "mbar", 0x00001000);
5940bc6746eSThomas Huth sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
5959743b581SPaolo Bonzini
596b0bed2c9SPeter Maydell s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
5975e077a77SThomas Huth m5206_timer_init(&s->timer[0], s->pic[9]);
5985e077a77SThomas Huth m5206_timer_init(&s->timer[1], s->pic[10]);
599f213ccc9SPhilippe Mathieu-Daudé s->uart[0] = mcf_uart_create(s->pic[12], serial_hd(0));
600f213ccc9SPhilippe Mathieu-Daudé s->uart[1] = mcf_uart_create(s->pic[13], serial_hd(1));
6019743b581SPaolo Bonzini }
6020bc6746eSThomas Huth
603989c8a46SPhilippe Mathieu-Daudé static Property mcf5206_mbar_properties[] = {
604989c8a46SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu,
605989c8a46SPhilippe Mathieu-Daudé TYPE_M68K_CPU, M68kCPU *),
606989c8a46SPhilippe Mathieu-Daudé DEFINE_PROP_END_OF_LIST(),
607989c8a46SPhilippe Mathieu-Daudé };
608989c8a46SPhilippe Mathieu-Daudé
mcf5206_mbar_class_init(ObjectClass * oc,void * data)6090bc6746eSThomas Huth static void mcf5206_mbar_class_init(ObjectClass *oc, void *data)
6100bc6746eSThomas Huth {
6110bc6746eSThomas Huth DeviceClass *dc = DEVICE_CLASS(oc);
6120bc6746eSThomas Huth
613989c8a46SPhilippe Mathieu-Daudé device_class_set_props(dc, mcf5206_mbar_properties);
6140bc6746eSThomas Huth set_bit(DEVICE_CATEGORY_MISC, dc->categories);
6150bc6746eSThomas Huth dc->desc = "MCF5206 system integration module";
6160bc6746eSThomas Huth dc->realize = mcf5206_mbar_realize;
617*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, m5206_mbar_reset);
6180bc6746eSThomas Huth }
6190bc6746eSThomas Huth
6200bc6746eSThomas Huth static const TypeInfo mcf5206_mbar_info = {
6210bc6746eSThomas Huth .name = TYPE_MCF5206_MBAR,
6220bc6746eSThomas Huth .parent = TYPE_SYS_BUS_DEVICE,
6230bc6746eSThomas Huth .instance_size = sizeof(m5206_mbar_state),
6240bc6746eSThomas Huth .class_init = mcf5206_mbar_class_init,
6250bc6746eSThomas Huth };
6260bc6746eSThomas Huth
mcf5206_mbar_register_types(void)6270bc6746eSThomas Huth static void mcf5206_mbar_register_types(void)
6280bc6746eSThomas Huth {
6290bc6746eSThomas Huth type_register_static(&mcf5206_mbar_info);
6300bc6746eSThomas Huth }
6310bc6746eSThomas Huth
6320bc6746eSThomas Huth type_init(mcf5206_mbar_register_types)
633