]>
Commit | Line | Data |
---|---|---|
8e093280 MCA |
1 | /* |
2 | * QEMU q800 logic GLUE (General Logic Unit) | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | * of this software and associated documentation files (the "Software"), to deal | |
6 | * in the Software without restriction, including without limitation the rights | |
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | * copies of the Software, and to permit persons to whom the Software is | |
9 | * furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | * THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include "qemu/osdep.h" | |
24 | #include "cpu.h" | |
25 | #include "hw/m68k/q800-glue.h" | |
26 | #include "hw/boards.h" | |
27 | #include "hw/irq.h" | |
28 | #include "hw/nmi.h" | |
29 | #include "hw/qdev-properties.h" | |
30 | #include "migration/vmstate.h" | |
31 | ||
32 | /* | |
33 | * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip | |
34 | * that performs a variety of functions (RAM management, clock generation, ...). | |
35 | * The GLUE chip receives interrupt requests from various devices, | |
36 | * assign priority to each, and asserts one or more interrupt line to the | |
37 | * CPU. | |
38 | */ | |
39 | ||
40 | /* | |
41 | * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes | |
42 | * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented | |
43 | * in NetBSD as follows: | |
44 | * | |
45 | * A/UX mode (Linux, NetBSD, auxmode GPIO low) | |
46 | * | |
47 | * Level 0: Spurious: ignored | |
48 | * Level 1: Software | |
49 | * Level 2: VIA2 (except ethernet, sound) | |
50 | * Level 3: Ethernet | |
51 | * Level 4: Serial (SCC) | |
52 | * Level 5: Sound | |
53 | * Level 6: VIA1 | |
54 | * Level 7: NMIs: parity errors, RESET button, YANCC error | |
55 | * | |
56 | * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high) | |
57 | * | |
58 | * Level 0: Spurious: ignored | |
59 | * Level 1: VIA1 (clock, ADB) | |
60 | * Level 2: VIA2 (NuBus, SCSI) | |
61 | * Level 3: | |
62 | * Level 4: Serial (SCC) | |
63 | * Level 5: | |
64 | * Level 6: | |
65 | * Level 7: Non-maskable: parity errors, RESET button | |
66 | * | |
67 | * Note that despite references to A/UX mode in Linux and NetBSD, at least | |
68 | * A/UX 3.0.1 still uses Classic mode. | |
69 | */ | |
70 | ||
71 | static void GLUE_set_irq(void *opaque, int irq, int level) | |
72 | { | |
73 | GLUEState *s = opaque; | |
74 | int i; | |
75 | ||
76 | if (s->auxmode) { | |
77 | /* Classic mode */ | |
78 | switch (irq) { | |
79 | case GLUE_IRQ_IN_VIA1: | |
80 | irq = 0; | |
81 | break; | |
82 | ||
83 | case GLUE_IRQ_IN_VIA2: | |
84 | irq = 1; | |
85 | break; | |
86 | ||
87 | case GLUE_IRQ_IN_SONIC: | |
88 | /* Route to VIA2 instead */ | |
89 | qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); | |
90 | return; | |
91 | ||
92 | case GLUE_IRQ_IN_ESCC: | |
93 | irq = 3; | |
94 | break; | |
95 | ||
96 | case GLUE_IRQ_IN_NMI: | |
97 | irq = 6; | |
98 | break; | |
99 | ||
9983f6e1 MCA |
100 | case GLUE_IRQ_IN_ASC: |
101 | /* Route to VIA2 instead, negative edge-triggered */ | |
102 | qemu_set_irq(s->irqs[GLUE_IRQ_ASC], !level); | |
103 | return; | |
104 | ||
8e093280 MCA |
105 | default: |
106 | g_assert_not_reached(); | |
107 | } | |
108 | } else { | |
109 | /* A/UX mode */ | |
110 | switch (irq) { | |
111 | case GLUE_IRQ_IN_VIA1: | |
112 | irq = 5; | |
113 | break; | |
114 | ||
115 | case GLUE_IRQ_IN_VIA2: | |
116 | irq = 1; | |
117 | break; | |
118 | ||
119 | case GLUE_IRQ_IN_SONIC: | |
120 | irq = 2; | |
121 | break; | |
122 | ||
123 | case GLUE_IRQ_IN_ESCC: | |
124 | irq = 3; | |
125 | break; | |
126 | ||
127 | case GLUE_IRQ_IN_NMI: | |
128 | irq = 6; | |
129 | break; | |
130 | ||
9983f6e1 MCA |
131 | case GLUE_IRQ_IN_ASC: |
132 | irq = 4; | |
133 | break; | |
134 | ||
8e093280 MCA |
135 | default: |
136 | g_assert_not_reached(); | |
137 | } | |
138 | } | |
139 | ||
140 | if (level) { | |
141 | s->ipr |= 1 << irq; | |
142 | } else { | |
143 | s->ipr &= ~(1 << irq); | |
144 | } | |
145 | ||
146 | for (i = 7; i >= 0; i--) { | |
147 | if ((s->ipr >> i) & 1) { | |
148 | m68k_set_irq_level(s->cpu, i + 1, i + 25); | |
149 | return; | |
150 | } | |
151 | } | |
152 | m68k_set_irq_level(s->cpu, 0, 0); | |
153 | } | |
154 | ||
155 | static void glue_auxmode_set_irq(void *opaque, int irq, int level) | |
156 | { | |
157 | GLUEState *s = GLUE(opaque); | |
158 | ||
159 | s->auxmode = level; | |
160 | } | |
161 | ||
162 | static void glue_nmi(NMIState *n, int cpu_index, Error **errp) | |
163 | { | |
164 | GLUEState *s = GLUE(n); | |
165 | ||
166 | /* Hold NMI active for 100ms */ | |
167 | GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1); | |
168 | timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); | |
169 | } | |
170 | ||
171 | static void glue_nmi_release(void *opaque) | |
172 | { | |
173 | GLUEState *s = GLUE(opaque); | |
174 | ||
175 | GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); | |
176 | } | |
177 | ||
d43e967f | 178 | static void glue_reset_hold(Object *obj) |
8e093280 | 179 | { |
d43e967f | 180 | GLUEState *s = GLUE(obj); |
8e093280 MCA |
181 | |
182 | s->ipr = 0; | |
183 | s->auxmode = 0; | |
184 | ||
185 | timer_del(s->nmi_release); | |
186 | } | |
187 | ||
188 | static const VMStateDescription vmstate_glue = { | |
189 | .name = "q800-glue", | |
190 | .version_id = 0, | |
191 | .minimum_version_id = 0, | |
192 | .fields = (VMStateField[]) { | |
193 | VMSTATE_UINT8(ipr, GLUEState), | |
194 | VMSTATE_UINT8(auxmode, GLUEState), | |
195 | VMSTATE_TIMER_PTR(nmi_release, GLUEState), | |
196 | VMSTATE_END_OF_LIST(), | |
197 | }, | |
198 | }; | |
199 | ||
200 | /* | |
201 | * If the m68k CPU implemented its inbound irq lines as GPIO lines | |
202 | * rather than via the m68k_set_irq_level() function we would not need | |
203 | * this cpu link property and could instead provide outbound IRQ lines | |
204 | * that the board could wire up to the CPU. | |
205 | */ | |
206 | static Property glue_properties[] = { | |
207 | DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), | |
208 | DEFINE_PROP_END_OF_LIST(), | |
209 | }; | |
210 | ||
211 | static void glue_finalize(Object *obj) | |
212 | { | |
213 | GLUEState *s = GLUE(obj); | |
214 | ||
215 | timer_free(s->nmi_release); | |
216 | } | |
217 | ||
218 | static void glue_init(Object *obj) | |
219 | { | |
220 | DeviceState *dev = DEVICE(obj); | |
221 | GLUEState *s = GLUE(dev); | |
222 | ||
223 | qdev_init_gpio_in(dev, GLUE_set_irq, 8); | |
224 | qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); | |
225 | ||
9983f6e1 | 226 | qdev_init_gpio_out(dev, s->irqs, 2); |
8e093280 MCA |
227 | |
228 | /* NMI release timer */ | |
229 | s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); | |
230 | } | |
231 | ||
232 | static void glue_class_init(ObjectClass *klass, void *data) | |
233 | { | |
234 | DeviceClass *dc = DEVICE_CLASS(klass); | |
d43e967f | 235 | ResettableClass *rc = RESETTABLE_CLASS(klass); |
8e093280 MCA |
236 | NMIClass *nc = NMI_CLASS(klass); |
237 | ||
238 | dc->vmsd = &vmstate_glue; | |
8e093280 | 239 | device_class_set_props(dc, glue_properties); |
d43e967f | 240 | rc->phases.hold = glue_reset_hold; |
8e093280 MCA |
241 | nc->nmi_monitor_handler = glue_nmi; |
242 | } | |
243 | ||
101b4764 MCA |
244 | static const TypeInfo glue_info_types[] = { |
245 | { | |
246 | .name = TYPE_GLUE, | |
247 | .parent = TYPE_SYS_BUS_DEVICE, | |
248 | .instance_size = sizeof(GLUEState), | |
249 | .instance_init = glue_init, | |
250 | .instance_finalize = glue_finalize, | |
251 | .class_init = glue_class_init, | |
252 | .interfaces = (InterfaceInfo[]) { | |
253 | { TYPE_NMI }, | |
254 | { } | |
255 | }, | |
8e093280 MCA |
256 | }, |
257 | }; | |
258 | ||
101b4764 | 259 | DEFINE_TYPES(glue_info_types) |