1 | /* |
2 | * QEMU Sparc SLAVIO aux io port emulation |
3 | * |
4 | * Copyright (c) 2005 Fabrice Bellard |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | |
25 | #include "qemu/osdep.h" |
26 | #include "hw/irq.h" |
27 | #include "hw/sysbus.h" |
28 | #include "migration/vmstate.h" |
29 | #include "qemu/module.h" |
30 | #include "sysemu/runstate.h" |
31 | #include "trace.h" |
32 | |
33 | /* |
34 | * This is the auxio port, chip control and system control part of |
35 | * chip STP2001 (Slave I/O), also produced as NCR89C105. See |
36 | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt |
37 | * |
38 | * This also includes the PMC CPU idle controller. |
39 | */ |
40 | |
41 | #define TYPE_SLAVIO_MISC "slavio_misc" |
42 | #define SLAVIO_MISC(obj) OBJECT_CHECK(MiscState, (obj), TYPE_SLAVIO_MISC) |
43 | |
44 | typedef struct MiscState { |
45 | SysBusDevice parent_obj; |
46 | |
47 | MemoryRegion cfg_iomem; |
48 | MemoryRegion diag_iomem; |
49 | MemoryRegion mdm_iomem; |
50 | MemoryRegion led_iomem; |
51 | MemoryRegion sysctrl_iomem; |
52 | MemoryRegion aux1_iomem; |
53 | MemoryRegion aux2_iomem; |
54 | qemu_irq irq; |
55 | qemu_irq fdc_tc; |
56 | uint32_t dummy; |
57 | uint8_t config; |
58 | uint8_t aux1, aux2; |
59 | uint8_t diag, mctrl; |
60 | uint8_t sysctrl; |
61 | uint16_t leds; |
62 | } MiscState; |
63 | |
64 | #define TYPE_APC "apc" |
65 | #define APC(obj) OBJECT_CHECK(APCState, (obj), TYPE_APC) |
66 | |
67 | typedef struct APCState { |
68 | SysBusDevice parent_obj; |
69 | |
70 | MemoryRegion iomem; |
71 | qemu_irq cpu_halt; |
72 | } APCState; |
73 | |
74 | #define MISC_SIZE 1 |
75 | #define LED_SIZE 2 |
76 | #define SYSCTRL_SIZE 4 |
77 | |
78 | #define AUX1_TC 0x02 |
79 | |
80 | #define AUX2_PWROFF 0x01 |
81 | #define AUX2_PWRINTCLR 0x02 |
82 | #define AUX2_PWRFAIL 0x20 |
83 | |
84 | #define CFG_PWRINTEN 0x08 |
85 | |
86 | #define SYS_RESET 0x01 |
87 | #define SYS_RESETSTAT 0x02 |
88 | |
89 | static void slavio_misc_update_irq(void *opaque) |
90 | { |
91 | MiscState *s = opaque; |
92 | |
93 | if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) { |
94 | trace_slavio_misc_update_irq_raise(); |
95 | qemu_irq_raise(s->irq); |
96 | } else { |
97 | trace_slavio_misc_update_irq_lower(); |
98 | qemu_irq_lower(s->irq); |
99 | } |
100 | } |
101 | |
102 | static void slavio_misc_reset(DeviceState *d) |
103 | { |
104 | MiscState *s = SLAVIO_MISC(d); |
105 | |
106 | // Diagnostic and system control registers not cleared in reset |
107 | s->config = s->aux1 = s->aux2 = s->mctrl = 0; |
108 | } |
109 | |
110 | static void slavio_set_power_fail(void *opaque, int irq, int power_failing) |
111 | { |
112 | MiscState *s = opaque; |
113 | |
114 | trace_slavio_set_power_fail(power_failing, s->config); |
115 | if (power_failing && (s->config & CFG_PWRINTEN)) { |
116 | s->aux2 |= AUX2_PWRFAIL; |
117 | } else { |
118 | s->aux2 &= ~AUX2_PWRFAIL; |
119 | } |
120 | slavio_misc_update_irq(s); |
121 | } |
122 | |
123 | static void slavio_cfg_mem_writeb(void *opaque, hwaddr addr, |
124 | uint64_t val, unsigned size) |
125 | { |
126 | MiscState *s = opaque; |
127 | |
128 | trace_slavio_cfg_mem_writeb(val & 0xff); |
129 | s->config = val & 0xff; |
130 | slavio_misc_update_irq(s); |
131 | } |
132 | |
133 | static uint64_t slavio_cfg_mem_readb(void *opaque, hwaddr addr, |
134 | unsigned size) |
135 | { |
136 | MiscState *s = opaque; |
137 | uint32_t ret = 0; |
138 | |
139 | ret = s->config; |
140 | trace_slavio_cfg_mem_readb(ret); |
141 | return ret; |
142 | } |
143 | |
144 | static const MemoryRegionOps slavio_cfg_mem_ops = { |
145 | .read = slavio_cfg_mem_readb, |
146 | .write = slavio_cfg_mem_writeb, |
147 | .endianness = DEVICE_NATIVE_ENDIAN, |
148 | .valid = { |
149 | .min_access_size = 1, |
150 | .max_access_size = 1, |
151 | }, |
152 | }; |
153 | |
154 | static void slavio_diag_mem_writeb(void *opaque, hwaddr addr, |
155 | uint64_t val, unsigned size) |
156 | { |
157 | MiscState *s = opaque; |
158 | |
159 | trace_slavio_diag_mem_writeb(val & 0xff); |
160 | s->diag = val & 0xff; |
161 | } |
162 | |
163 | static uint64_t slavio_diag_mem_readb(void *opaque, hwaddr addr, |
164 | unsigned size) |
165 | { |
166 | MiscState *s = opaque; |
167 | uint32_t ret = 0; |
168 | |
169 | ret = s->diag; |
170 | trace_slavio_diag_mem_readb(ret); |
171 | return ret; |
172 | } |
173 | |
174 | static const MemoryRegionOps slavio_diag_mem_ops = { |
175 | .read = slavio_diag_mem_readb, |
176 | .write = slavio_diag_mem_writeb, |
177 | .endianness = DEVICE_NATIVE_ENDIAN, |
178 | .valid = { |
179 | .min_access_size = 1, |
180 | .max_access_size = 1, |
181 | }, |
182 | }; |
183 | |
184 | static void slavio_mdm_mem_writeb(void *opaque, hwaddr addr, |
185 | uint64_t val, unsigned size) |
186 | { |
187 | MiscState *s = opaque; |
188 | |
189 | trace_slavio_mdm_mem_writeb(val & 0xff); |
190 | s->mctrl = val & 0xff; |
191 | } |
192 | |
193 | static uint64_t slavio_mdm_mem_readb(void *opaque, hwaddr addr, |
194 | unsigned size) |
195 | { |
196 | MiscState *s = opaque; |
197 | uint32_t ret = 0; |
198 | |
199 | ret = s->mctrl; |
200 | trace_slavio_mdm_mem_readb(ret); |
201 | return ret; |
202 | } |
203 | |
204 | static const MemoryRegionOps slavio_mdm_mem_ops = { |
205 | .read = slavio_mdm_mem_readb, |
206 | .write = slavio_mdm_mem_writeb, |
207 | .endianness = DEVICE_NATIVE_ENDIAN, |
208 | .valid = { |
209 | .min_access_size = 1, |
210 | .max_access_size = 1, |
211 | }, |
212 | }; |
213 | |
214 | static void slavio_aux1_mem_writeb(void *opaque, hwaddr addr, |
215 | uint64_t val, unsigned size) |
216 | { |
217 | MiscState *s = opaque; |
218 | |
219 | trace_slavio_aux1_mem_writeb(val & 0xff); |
220 | if (val & AUX1_TC) { |
221 | // Send a pulse to floppy terminal count line |
222 | if (s->fdc_tc) { |
223 | qemu_irq_raise(s->fdc_tc); |
224 | qemu_irq_lower(s->fdc_tc); |
225 | } |
226 | val &= ~AUX1_TC; |
227 | } |
228 | s->aux1 = val & 0xff; |
229 | } |
230 | |
231 | static uint64_t slavio_aux1_mem_readb(void *opaque, hwaddr addr, |
232 | unsigned size) |
233 | { |
234 | MiscState *s = opaque; |
235 | uint32_t ret = 0; |
236 | |
237 | ret = s->aux1; |
238 | trace_slavio_aux1_mem_readb(ret); |
239 | return ret; |
240 | } |
241 | |
242 | static const MemoryRegionOps slavio_aux1_mem_ops = { |
243 | .read = slavio_aux1_mem_readb, |
244 | .write = slavio_aux1_mem_writeb, |
245 | .endianness = DEVICE_NATIVE_ENDIAN, |
246 | .valid = { |
247 | .min_access_size = 1, |
248 | .max_access_size = 1, |
249 | }, |
250 | }; |
251 | |
252 | static void slavio_aux2_mem_writeb(void *opaque, hwaddr addr, |
253 | uint64_t val, unsigned size) |
254 | { |
255 | MiscState *s = opaque; |
256 | |
257 | val &= AUX2_PWRINTCLR | AUX2_PWROFF; |
258 | trace_slavio_aux2_mem_writeb(val & 0xff); |
259 | val |= s->aux2 & AUX2_PWRFAIL; |
260 | if (val & AUX2_PWRINTCLR) // Clear Power Fail int |
261 | val &= AUX2_PWROFF; |
262 | s->aux2 = val; |
263 | if (val & AUX2_PWROFF) |
264 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); |
265 | slavio_misc_update_irq(s); |
266 | } |
267 | |
268 | static uint64_t slavio_aux2_mem_readb(void *opaque, hwaddr addr, |
269 | unsigned size) |
270 | { |
271 | MiscState *s = opaque; |
272 | uint32_t ret = 0; |
273 | |
274 | ret = s->aux2; |
275 | trace_slavio_aux2_mem_readb(ret); |
276 | return ret; |
277 | } |
278 | |
279 | static const MemoryRegionOps slavio_aux2_mem_ops = { |
280 | .read = slavio_aux2_mem_readb, |
281 | .write = slavio_aux2_mem_writeb, |
282 | .endianness = DEVICE_NATIVE_ENDIAN, |
283 | .valid = { |
284 | .min_access_size = 1, |
285 | .max_access_size = 1, |
286 | }, |
287 | }; |
288 | |
289 | static void apc_mem_writeb(void *opaque, hwaddr addr, |
290 | uint64_t val, unsigned size) |
291 | { |
292 | APCState *s = opaque; |
293 | |
294 | trace_apc_mem_writeb(val & 0xff); |
295 | qemu_irq_raise(s->cpu_halt); |
296 | } |
297 | |
298 | static uint64_t apc_mem_readb(void *opaque, hwaddr addr, |
299 | unsigned size) |
300 | { |
301 | uint32_t ret = 0; |
302 | |
303 | trace_apc_mem_readb(ret); |
304 | return ret; |
305 | } |
306 | |
307 | static const MemoryRegionOps apc_mem_ops = { |
308 | .read = apc_mem_readb, |
309 | .write = apc_mem_writeb, |
310 | .endianness = DEVICE_NATIVE_ENDIAN, |
311 | .valid = { |
312 | .min_access_size = 1, |
313 | .max_access_size = 1, |
314 | } |
315 | }; |
316 | |
317 | static uint64_t slavio_sysctrl_mem_readl(void *opaque, hwaddr addr, |
318 | unsigned size) |
319 | { |
320 | MiscState *s = opaque; |
321 | uint32_t ret = 0; |
322 | |
323 | switch (addr) { |
324 | case 0: |
325 | ret = s->sysctrl; |
326 | break; |
327 | default: |
328 | break; |
329 | } |
330 | trace_slavio_sysctrl_mem_readl(ret); |
331 | return ret; |
332 | } |
333 | |
334 | static void slavio_sysctrl_mem_writel(void *opaque, hwaddr addr, |
335 | uint64_t val, unsigned size) |
336 | { |
337 | MiscState *s = opaque; |
338 | |
339 | trace_slavio_sysctrl_mem_writel(val); |
340 | switch (addr) { |
341 | case 0: |
342 | if (val & SYS_RESET) { |
343 | s->sysctrl = SYS_RESETSTAT; |
344 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
345 | } |
346 | break; |
347 | default: |
348 | break; |
349 | } |
350 | } |
351 | |
352 | static const MemoryRegionOps slavio_sysctrl_mem_ops = { |
353 | .read = slavio_sysctrl_mem_readl, |
354 | .write = slavio_sysctrl_mem_writel, |
355 | .endianness = DEVICE_NATIVE_ENDIAN, |
356 | .valid = { |
357 | .min_access_size = 4, |
358 | .max_access_size = 4, |
359 | }, |
360 | }; |
361 | |
362 | static uint64_t slavio_led_mem_readw(void *opaque, hwaddr addr, |
363 | unsigned size) |
364 | { |
365 | MiscState *s = opaque; |
366 | uint32_t ret = 0; |
367 | |
368 | switch (addr) { |
369 | case 0: |
370 | ret = s->leds; |
371 | break; |
372 | default: |
373 | break; |
374 | } |
375 | trace_slavio_led_mem_readw(ret); |
376 | return ret; |
377 | } |
378 | |
379 | static void slavio_led_mem_writew(void *opaque, hwaddr addr, |
380 | uint64_t val, unsigned size) |
381 | { |
382 | MiscState *s = opaque; |
383 | |
384 | trace_slavio_led_mem_writew(val & 0xffff); |
385 | switch (addr) { |
386 | case 0: |
387 | s->leds = val; |
388 | break; |
389 | default: |
390 | break; |
391 | } |
392 | } |
393 | |
394 | static const MemoryRegionOps slavio_led_mem_ops = { |
395 | .read = slavio_led_mem_readw, |
396 | .write = slavio_led_mem_writew, |
397 | .endianness = DEVICE_NATIVE_ENDIAN, |
398 | .valid = { |
399 | .min_access_size = 2, |
400 | .max_access_size = 2, |
401 | }, |
402 | }; |
403 | |
404 | static const VMStateDescription vmstate_misc = { |
405 | .name ="slavio_misc" , |
406 | .version_id = 1, |
407 | .minimum_version_id = 1, |
408 | .fields = (VMStateField[]) { |
409 | VMSTATE_UINT32(dummy, MiscState), |
410 | VMSTATE_UINT8(config, MiscState), |
411 | VMSTATE_UINT8(aux1, MiscState), |
412 | VMSTATE_UINT8(aux2, MiscState), |
413 | VMSTATE_UINT8(diag, MiscState), |
414 | VMSTATE_UINT8(mctrl, MiscState), |
415 | VMSTATE_UINT8(sysctrl, MiscState), |
416 | VMSTATE_END_OF_LIST() |
417 | } |
418 | }; |
419 | |
420 | static void apc_init(Object *obj) |
421 | { |
422 | APCState *s = APC(obj); |
423 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
424 | |
425 | sysbus_init_irq(dev, &s->cpu_halt); |
426 | |
427 | /* Power management (APC) XXX: not a Slavio device */ |
428 | memory_region_init_io(&s->iomem, obj, &apc_mem_ops, s, |
429 | "apc" , MISC_SIZE); |
430 | sysbus_init_mmio(dev, &s->iomem); |
431 | } |
432 | |
433 | static void slavio_misc_init(Object *obj) |
434 | { |
435 | DeviceState *dev = DEVICE(obj); |
436 | MiscState *s = SLAVIO_MISC(obj); |
437 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
438 | |
439 | sysbus_init_irq(sbd, &s->irq); |
440 | sysbus_init_irq(sbd, &s->fdc_tc); |
441 | |
442 | /* 8 bit registers */ |
443 | /* Slavio control */ |
444 | memory_region_init_io(&s->cfg_iomem, obj, &slavio_cfg_mem_ops, s, |
445 | "configuration" , MISC_SIZE); |
446 | sysbus_init_mmio(sbd, &s->cfg_iomem); |
447 | |
448 | /* Diagnostics */ |
449 | memory_region_init_io(&s->diag_iomem, obj, &slavio_diag_mem_ops, s, |
450 | "diagnostic" , MISC_SIZE); |
451 | sysbus_init_mmio(sbd, &s->diag_iomem); |
452 | |
453 | /* Modem control */ |
454 | memory_region_init_io(&s->mdm_iomem, obj, &slavio_mdm_mem_ops, s, |
455 | "modem" , MISC_SIZE); |
456 | sysbus_init_mmio(sbd, &s->mdm_iomem); |
457 | |
458 | /* 16 bit registers */ |
459 | /* ss600mp diag LEDs */ |
460 | memory_region_init_io(&s->led_iomem, obj, &slavio_led_mem_ops, s, |
461 | "leds" , LED_SIZE); |
462 | sysbus_init_mmio(sbd, &s->led_iomem); |
463 | |
464 | /* 32 bit registers */ |
465 | /* System control */ |
466 | memory_region_init_io(&s->sysctrl_iomem, obj, &slavio_sysctrl_mem_ops, s, |
467 | "system-control" , SYSCTRL_SIZE); |
468 | sysbus_init_mmio(sbd, &s->sysctrl_iomem); |
469 | |
470 | /* AUX 1 (Misc System Functions) */ |
471 | memory_region_init_io(&s->aux1_iomem, obj, &slavio_aux1_mem_ops, s, |
472 | "misc-system-functions" , MISC_SIZE); |
473 | sysbus_init_mmio(sbd, &s->aux1_iomem); |
474 | |
475 | /* AUX 2 (Software Powerdown Control) */ |
476 | memory_region_init_io(&s->aux2_iomem, obj, &slavio_aux2_mem_ops, s, |
477 | "software-powerdown-control" , MISC_SIZE); |
478 | sysbus_init_mmio(sbd, &s->aux2_iomem); |
479 | |
480 | qdev_init_gpio_in(dev, slavio_set_power_fail, 1); |
481 | } |
482 | |
483 | static void slavio_misc_class_init(ObjectClass *klass, void *data) |
484 | { |
485 | DeviceClass *dc = DEVICE_CLASS(klass); |
486 | |
487 | dc->reset = slavio_misc_reset; |
488 | dc->vmsd = &vmstate_misc; |
489 | } |
490 | |
491 | static const TypeInfo slavio_misc_info = { |
492 | .name = TYPE_SLAVIO_MISC, |
493 | .parent = TYPE_SYS_BUS_DEVICE, |
494 | .instance_size = sizeof(MiscState), |
495 | .instance_init = slavio_misc_init, |
496 | .class_init = slavio_misc_class_init, |
497 | }; |
498 | |
499 | static const TypeInfo apc_info = { |
500 | .name = TYPE_APC, |
501 | .parent = TYPE_SYS_BUS_DEVICE, |
502 | .instance_size = sizeof(MiscState), |
503 | .instance_init = apc_init, |
504 | }; |
505 | |
506 | static void slavio_misc_register_types(void) |
507 | { |
508 | type_register_static(&slavio_misc_info); |
509 | type_register_static(&apc_info); |
510 | } |
511 | |
512 | type_init(slavio_misc_register_types) |
513 | |