| /* |
| * 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, ResetType type) |
| { |
| 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) |