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