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