/* * QEMU q800 logic GLUE (General Logic Unit) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "cpu.h" #include "hw/m68k/q800-glue.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/nmi.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" /* * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip * that performs a variety of functions (RAM management, clock generation, ...). * The GLUE chip receives interrupt requests from various devices, * assign priority to each, and asserts one or more interrupt line to the * CPU. */ /* * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented * in NetBSD as follows: * * A/UX mode (Linux, NetBSD, auxmode GPIO low) * * Level 0: Spurious: ignored * Level 1: Software * Level 2: VIA2 (except ethernet, sound) * Level 3: Ethernet * Level 4: Serial (SCC) * Level 5: Sound * Level 6: VIA1 * Level 7: NMIs: parity errors, RESET button, YANCC error * * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high) * * Level 0: Spurious: ignored * Level 1: VIA1 (clock, ADB) * Level 2: VIA2 (NuBus, SCSI) * Level 3: * Level 4: Serial (SCC) * Level 5: * Level 6: * Level 7: Non-maskable: parity errors, RESET button * * Note that despite references to A/UX mode in Linux and NetBSD, at least * A/UX 3.0.1 still uses Classic mode. */ static void GLUE_set_irq(void *opaque, int irq, int level) { GLUEState *s = opaque; int i; if (s->auxmode) { /* Classic mode */ switch (irq) { case GLUE_IRQ_IN_VIA1: irq = 0; break; case GLUE_IRQ_IN_VIA2: irq = 1; break; case GLUE_IRQ_IN_SONIC: /* Route to VIA2 instead */ qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); return; case GLUE_IRQ_IN_ESCC: irq = 3; break; case GLUE_IRQ_IN_NMI: irq = 6; break; case GLUE_IRQ_IN_ASC: /* Route to VIA2 instead, negative edge-triggered */ qemu_set_irq(s->irqs[GLUE_IRQ_ASC], !level); return; default: g_assert_not_reached(); } } else { /* A/UX mode */ switch (irq) { case GLUE_IRQ_IN_VIA1: irq = 5; break; case GLUE_IRQ_IN_VIA2: irq = 1; break; case GLUE_IRQ_IN_SONIC: irq = 2; break; case GLUE_IRQ_IN_ESCC: irq = 3; break; case GLUE_IRQ_IN_NMI: irq = 6; break; case GLUE_IRQ_IN_ASC: irq = 4; break; default: g_assert_not_reached(); } } if (level) { s->ipr |= 1 << irq; } else { s->ipr &= ~(1 << irq); } for (i = 7; i >= 0; i--) { if ((s->ipr >> i) & 1) { m68k_set_irq_level(s->cpu, i + 1, i + 25); return; } } m68k_set_irq_level(s->cpu, 0, 0); } static void glue_auxmode_set_irq(void *opaque, int irq, int level) { GLUEState *s = GLUE(opaque); s->auxmode = level; } static void glue_nmi(NMIState *n, int cpu_index, Error **errp) { GLUEState *s = GLUE(n); /* Hold NMI active for 100ms */ GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1); timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); } static void glue_nmi_release(void *opaque) { GLUEState *s = GLUE(opaque); GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); } static void glue_reset_hold(Object *obj) { GLUEState *s = GLUE(obj); s->ipr = 0; s->auxmode = 0; timer_del(s->nmi_release); } static const VMStateDescription vmstate_glue = { .name = "q800-glue", .version_id = 0, .minimum_version_id = 0, .fields = (const VMStateField[]) { VMSTATE_UINT8(ipr, GLUEState), VMSTATE_UINT8(auxmode, GLUEState), VMSTATE_TIMER_PTR(nmi_release, GLUEState), VMSTATE_END_OF_LIST(), }, }; /* * If the m68k CPU implemented its inbound irq lines as GPIO lines * rather than via the m68k_set_irq_level() function we would not need * this cpu link property and could instead provide outbound IRQ lines * that the board could wire up to the CPU. */ static Property glue_properties[] = { DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), DEFINE_PROP_END_OF_LIST(), }; static void glue_finalize(Object *obj) { GLUEState *s = GLUE(obj); timer_free(s->nmi_release); } static void glue_init(Object *obj) { DeviceState *dev = DEVICE(obj); GLUEState *s = GLUE(dev); qdev_init_gpio_in(dev, GLUE_set_irq, 8); qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); qdev_init_gpio_out(dev, s->irqs, 2); /* NMI release timer */ s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); } static void glue_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); NMIClass *nc = NMI_CLASS(klass); dc->vmsd = &vmstate_glue; device_class_set_props(dc, glue_properties); rc->phases.hold = glue_reset_hold; nc->nmi_monitor_handler = glue_nmi; } static const TypeInfo glue_info_types[] = { { .name = TYPE_GLUE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GLUEState), .instance_init = glue_init, .instance_finalize = glue_finalize, .class_init = glue_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, { } }, }, }; DEFINE_TYPES(glue_info_types)