|
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 #include "hw.h" |
|
25 #include "sun4m.h" |
|
26 #include "sysemu.h" |
|
27 |
|
28 /* debug misc */ |
|
29 //#define DEBUG_MISC |
|
30 |
|
31 /* |
|
32 * This is the auxio port, chip control and system control part of |
|
33 * chip STP2001 (Slave I/O), also produced as NCR89C105. See |
|
34 * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt |
|
35 * |
|
36 * This also includes the PMC CPU idle controller. |
|
37 */ |
|
38 |
|
39 #ifdef DEBUG_MISC |
|
40 #define MISC_DPRINTF(fmt, args...) \ |
|
41 do { printf("MISC: " fmt , ##args); } while (0) |
|
42 #else |
|
43 #define MISC_DPRINTF(fmt, args...) |
|
44 #endif |
|
45 |
|
46 typedef struct MiscState { |
|
47 qemu_irq irq; |
|
48 uint8_t config; |
|
49 uint8_t aux1, aux2; |
|
50 uint8_t diag, mctrl; |
|
51 uint32_t sysctrl; |
|
52 uint16_t leds; |
|
53 qemu_irq cpu_halt; |
|
54 qemu_irq fdc_tc; |
|
55 } MiscState; |
|
56 |
|
57 #define MISC_SIZE 1 |
|
58 #define SYSCTRL_SIZE 4 |
|
59 #define LED_MAXADDR 1 |
|
60 #define LED_SIZE (LED_MAXADDR + 1) |
|
61 |
|
62 #define MISC_MASK 0x0fff0000 |
|
63 #define MISC_LEDS 0x01600000 |
|
64 #define MISC_CFG 0x01800000 |
|
65 #define MISC_DIAG 0x01a00000 |
|
66 #define MISC_MDM 0x01b00000 |
|
67 #define MISC_SYS 0x01f00000 |
|
68 |
|
69 #define AUX1_TC 0x02 |
|
70 |
|
71 #define AUX2_PWROFF 0x01 |
|
72 #define AUX2_PWRINTCLR 0x02 |
|
73 #define AUX2_PWRFAIL 0x20 |
|
74 |
|
75 #define CFG_PWRINTEN 0x08 |
|
76 |
|
77 #define SYS_RESET 0x01 |
|
78 #define SYS_RESETSTAT 0x02 |
|
79 |
|
80 static void slavio_misc_update_irq(void *opaque) |
|
81 { |
|
82 MiscState *s = opaque; |
|
83 |
|
84 if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) { |
|
85 MISC_DPRINTF("Raise IRQ\n"); |
|
86 qemu_irq_raise(s->irq); |
|
87 } else { |
|
88 MISC_DPRINTF("Lower IRQ\n"); |
|
89 qemu_irq_lower(s->irq); |
|
90 } |
|
91 } |
|
92 |
|
93 static void slavio_misc_reset(void *opaque) |
|
94 { |
|
95 MiscState *s = opaque; |
|
96 |
|
97 // Diagnostic and system control registers not cleared in reset |
|
98 s->config = s->aux1 = s->aux2 = s->mctrl = 0; |
|
99 } |
|
100 |
|
101 void slavio_set_power_fail(void *opaque, int power_failing) |
|
102 { |
|
103 MiscState *s = opaque; |
|
104 |
|
105 MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); |
|
106 if (power_failing && (s->config & CFG_PWRINTEN)) { |
|
107 s->aux2 |= AUX2_PWRFAIL; |
|
108 } else { |
|
109 s->aux2 &= ~AUX2_PWRFAIL; |
|
110 } |
|
111 slavio_misc_update_irq(s); |
|
112 } |
|
113 |
|
114 static void slavio_cfg_mem_writeb(void *opaque, target_phys_addr_t addr, |
|
115 uint32_t val) |
|
116 { |
|
117 MiscState *s = opaque; |
|
118 |
|
119 MISC_DPRINTF("Write config %2.2x\n", val & 0xff); |
|
120 s->config = val & 0xff; |
|
121 slavio_misc_update_irq(s); |
|
122 } |
|
123 |
|
124 static uint32_t slavio_cfg_mem_readb(void *opaque, target_phys_addr_t addr) |
|
125 { |
|
126 MiscState *s = opaque; |
|
127 uint32_t ret = 0; |
|
128 |
|
129 ret = s->config; |
|
130 MISC_DPRINTF("Read config %2.2x\n", ret); |
|
131 return ret; |
|
132 } |
|
133 |
|
134 static CPUReadMemoryFunc *slavio_cfg_mem_read[3] = { |
|
135 slavio_cfg_mem_readb, |
|
136 NULL, |
|
137 NULL, |
|
138 }; |
|
139 |
|
140 static CPUWriteMemoryFunc *slavio_cfg_mem_write[3] = { |
|
141 slavio_cfg_mem_writeb, |
|
142 NULL, |
|
143 NULL, |
|
144 }; |
|
145 |
|
146 static void slavio_diag_mem_writeb(void *opaque, target_phys_addr_t addr, |
|
147 uint32_t val) |
|
148 { |
|
149 MiscState *s = opaque; |
|
150 |
|
151 MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); |
|
152 s->diag = val & 0xff; |
|
153 } |
|
154 |
|
155 static uint32_t slavio_diag_mem_readb(void *opaque, target_phys_addr_t addr) |
|
156 { |
|
157 MiscState *s = opaque; |
|
158 uint32_t ret = 0; |
|
159 |
|
160 ret = s->diag; |
|
161 MISC_DPRINTF("Read diag %2.2x\n", ret); |
|
162 return ret; |
|
163 } |
|
164 |
|
165 static CPUReadMemoryFunc *slavio_diag_mem_read[3] = { |
|
166 slavio_diag_mem_readb, |
|
167 NULL, |
|
168 NULL, |
|
169 }; |
|
170 |
|
171 static CPUWriteMemoryFunc *slavio_diag_mem_write[3] = { |
|
172 slavio_diag_mem_writeb, |
|
173 NULL, |
|
174 NULL, |
|
175 }; |
|
176 |
|
177 static void slavio_mdm_mem_writeb(void *opaque, target_phys_addr_t addr, |
|
178 uint32_t val) |
|
179 { |
|
180 MiscState *s = opaque; |
|
181 |
|
182 MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); |
|
183 s->mctrl = val & 0xff; |
|
184 } |
|
185 |
|
186 static uint32_t slavio_mdm_mem_readb(void *opaque, target_phys_addr_t addr) |
|
187 { |
|
188 MiscState *s = opaque; |
|
189 uint32_t ret = 0; |
|
190 |
|
191 ret = s->mctrl; |
|
192 MISC_DPRINTF("Read modem control %2.2x\n", ret); |
|
193 return ret; |
|
194 } |
|
195 |
|
196 static CPUReadMemoryFunc *slavio_mdm_mem_read[3] = { |
|
197 slavio_mdm_mem_readb, |
|
198 NULL, |
|
199 NULL, |
|
200 }; |
|
201 |
|
202 static CPUWriteMemoryFunc *slavio_mdm_mem_write[3] = { |
|
203 slavio_mdm_mem_writeb, |
|
204 NULL, |
|
205 NULL, |
|
206 }; |
|
207 |
|
208 static void slavio_aux1_mem_writeb(void *opaque, target_phys_addr_t addr, |
|
209 uint32_t val) |
|
210 { |
|
211 MiscState *s = opaque; |
|
212 |
|
213 MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); |
|
214 if (val & AUX1_TC) { |
|
215 // Send a pulse to floppy terminal count line |
|
216 if (s->fdc_tc) { |
|
217 qemu_irq_raise(s->fdc_tc); |
|
218 qemu_irq_lower(s->fdc_tc); |
|
219 } |
|
220 val &= ~AUX1_TC; |
|
221 } |
|
222 s->aux1 = val & 0xff; |
|
223 } |
|
224 |
|
225 static uint32_t slavio_aux1_mem_readb(void *opaque, target_phys_addr_t addr) |
|
226 { |
|
227 MiscState *s = opaque; |
|
228 uint32_t ret = 0; |
|
229 |
|
230 ret = s->aux1; |
|
231 MISC_DPRINTF("Read aux1 %2.2x\n", ret); |
|
232 |
|
233 return ret; |
|
234 } |
|
235 |
|
236 static CPUReadMemoryFunc *slavio_aux1_mem_read[3] = { |
|
237 slavio_aux1_mem_readb, |
|
238 NULL, |
|
239 NULL, |
|
240 }; |
|
241 |
|
242 static CPUWriteMemoryFunc *slavio_aux1_mem_write[3] = { |
|
243 slavio_aux1_mem_writeb, |
|
244 NULL, |
|
245 NULL, |
|
246 }; |
|
247 |
|
248 static void slavio_aux2_mem_writeb(void *opaque, target_phys_addr_t addr, |
|
249 uint32_t val) |
|
250 { |
|
251 MiscState *s = opaque; |
|
252 |
|
253 val &= AUX2_PWRINTCLR | AUX2_PWROFF; |
|
254 MISC_DPRINTF("Write aux2 %2.2x\n", val); |
|
255 val |= s->aux2 & AUX2_PWRFAIL; |
|
256 if (val & AUX2_PWRINTCLR) // Clear Power Fail int |
|
257 val &= AUX2_PWROFF; |
|
258 s->aux2 = val; |
|
259 if (val & AUX2_PWROFF) |
|
260 qemu_system_shutdown_request(); |
|
261 slavio_misc_update_irq(s); |
|
262 } |
|
263 |
|
264 static uint32_t slavio_aux2_mem_readb(void *opaque, target_phys_addr_t addr) |
|
265 { |
|
266 MiscState *s = opaque; |
|
267 uint32_t ret = 0; |
|
268 |
|
269 ret = s->aux2; |
|
270 MISC_DPRINTF("Read aux2 %2.2x\n", ret); |
|
271 |
|
272 return ret; |
|
273 } |
|
274 |
|
275 static CPUReadMemoryFunc *slavio_aux2_mem_read[3] = { |
|
276 slavio_aux2_mem_readb, |
|
277 NULL, |
|
278 NULL, |
|
279 }; |
|
280 |
|
281 static CPUWriteMemoryFunc *slavio_aux2_mem_write[3] = { |
|
282 slavio_aux2_mem_writeb, |
|
283 NULL, |
|
284 NULL, |
|
285 }; |
|
286 |
|
287 static void apc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
288 { |
|
289 MiscState *s = opaque; |
|
290 |
|
291 MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); |
|
292 qemu_irq_raise(s->cpu_halt); |
|
293 } |
|
294 |
|
295 static uint32_t apc_mem_readb(void *opaque, target_phys_addr_t addr) |
|
296 { |
|
297 uint32_t ret = 0; |
|
298 |
|
299 MISC_DPRINTF("Read power management %2.2x\n", ret); |
|
300 return ret; |
|
301 } |
|
302 |
|
303 static CPUReadMemoryFunc *apc_mem_read[3] = { |
|
304 apc_mem_readb, |
|
305 NULL, |
|
306 NULL, |
|
307 }; |
|
308 |
|
309 static CPUWriteMemoryFunc *apc_mem_write[3] = { |
|
310 apc_mem_writeb, |
|
311 NULL, |
|
312 NULL, |
|
313 }; |
|
314 |
|
315 static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr) |
|
316 { |
|
317 MiscState *s = opaque; |
|
318 uint32_t ret = 0; |
|
319 |
|
320 switch (addr) { |
|
321 case 0: |
|
322 ret = s->sysctrl; |
|
323 break; |
|
324 default: |
|
325 break; |
|
326 } |
|
327 MISC_DPRINTF("Read system control %08x\n", ret); |
|
328 return ret; |
|
329 } |
|
330 |
|
331 static void slavio_sysctrl_mem_writel(void *opaque, target_phys_addr_t addr, |
|
332 uint32_t val) |
|
333 { |
|
334 MiscState *s = opaque; |
|
335 |
|
336 MISC_DPRINTF("Write system control %08x\n", val); |
|
337 switch (addr) { |
|
338 case 0: |
|
339 if (val & SYS_RESET) { |
|
340 s->sysctrl = SYS_RESETSTAT; |
|
341 qemu_system_reset_request(); |
|
342 } |
|
343 break; |
|
344 default: |
|
345 break; |
|
346 } |
|
347 } |
|
348 |
|
349 static CPUReadMemoryFunc *slavio_sysctrl_mem_read[3] = { |
|
350 NULL, |
|
351 NULL, |
|
352 slavio_sysctrl_mem_readl, |
|
353 }; |
|
354 |
|
355 static CPUWriteMemoryFunc *slavio_sysctrl_mem_write[3] = { |
|
356 NULL, |
|
357 NULL, |
|
358 slavio_sysctrl_mem_writel, |
|
359 }; |
|
360 |
|
361 static uint32_t slavio_led_mem_readw(void *opaque, target_phys_addr_t addr) |
|
362 { |
|
363 MiscState *s = opaque; |
|
364 uint32_t ret = 0; |
|
365 |
|
366 switch (addr) { |
|
367 case 0: |
|
368 ret = s->leds; |
|
369 break; |
|
370 default: |
|
371 break; |
|
372 } |
|
373 MISC_DPRINTF("Read diagnostic LED %04x\n", ret); |
|
374 return ret; |
|
375 } |
|
376 |
|
377 static void slavio_led_mem_writew(void *opaque, target_phys_addr_t addr, |
|
378 uint32_t val) |
|
379 { |
|
380 MiscState *s = opaque; |
|
381 |
|
382 MISC_DPRINTF("Write diagnostic LED %04x\n", val & 0xffff); |
|
383 switch (addr) { |
|
384 case 0: |
|
385 s->leds = val; |
|
386 break; |
|
387 default: |
|
388 break; |
|
389 } |
|
390 } |
|
391 |
|
392 static CPUReadMemoryFunc *slavio_led_mem_read[3] = { |
|
393 NULL, |
|
394 slavio_led_mem_readw, |
|
395 NULL, |
|
396 }; |
|
397 |
|
398 static CPUWriteMemoryFunc *slavio_led_mem_write[3] = { |
|
399 NULL, |
|
400 slavio_led_mem_writew, |
|
401 NULL, |
|
402 }; |
|
403 |
|
404 static void slavio_misc_save(QEMUFile *f, void *opaque) |
|
405 { |
|
406 MiscState *s = opaque; |
|
407 uint32_t tmp = 0; |
|
408 uint8_t tmp8; |
|
409 |
|
410 qemu_put_be32s(f, &tmp); /* ignored, was IRQ. */ |
|
411 qemu_put_8s(f, &s->config); |
|
412 qemu_put_8s(f, &s->aux1); |
|
413 qemu_put_8s(f, &s->aux2); |
|
414 qemu_put_8s(f, &s->diag); |
|
415 qemu_put_8s(f, &s->mctrl); |
|
416 tmp8 = s->sysctrl & 0xff; |
|
417 qemu_put_8s(f, &tmp8); |
|
418 } |
|
419 |
|
420 static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id) |
|
421 { |
|
422 MiscState *s = opaque; |
|
423 uint32_t tmp; |
|
424 uint8_t tmp8; |
|
425 |
|
426 if (version_id != 1) |
|
427 return -EINVAL; |
|
428 |
|
429 qemu_get_be32s(f, &tmp); |
|
430 qemu_get_8s(f, &s->config); |
|
431 qemu_get_8s(f, &s->aux1); |
|
432 qemu_get_8s(f, &s->aux2); |
|
433 qemu_get_8s(f, &s->diag); |
|
434 qemu_get_8s(f, &s->mctrl); |
|
435 qemu_get_8s(f, &tmp8); |
|
436 s->sysctrl = (uint32_t)tmp8; |
|
437 return 0; |
|
438 } |
|
439 |
|
440 void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base, |
|
441 target_phys_addr_t aux1_base, |
|
442 target_phys_addr_t aux2_base, qemu_irq irq, |
|
443 qemu_irq cpu_halt, qemu_irq **fdc_tc) |
|
444 { |
|
445 int io; |
|
446 MiscState *s; |
|
447 |
|
448 s = qemu_mallocz(sizeof(MiscState)); |
|
449 if (!s) |
|
450 return NULL; |
|
451 |
|
452 if (base) { |
|
453 /* 8 bit registers */ |
|
454 |
|
455 // Slavio control |
|
456 io = cpu_register_io_memory(0, slavio_cfg_mem_read, |
|
457 slavio_cfg_mem_write, s); |
|
458 cpu_register_physical_memory(base + MISC_CFG, MISC_SIZE, io); |
|
459 |
|
460 // Diagnostics |
|
461 io = cpu_register_io_memory(0, slavio_diag_mem_read, |
|
462 slavio_diag_mem_write, s); |
|
463 cpu_register_physical_memory(base + MISC_DIAG, MISC_SIZE, io); |
|
464 |
|
465 // Modem control |
|
466 io = cpu_register_io_memory(0, slavio_mdm_mem_read, |
|
467 slavio_mdm_mem_write, s); |
|
468 cpu_register_physical_memory(base + MISC_MDM, MISC_SIZE, io); |
|
469 |
|
470 /* 16 bit registers */ |
|
471 io = cpu_register_io_memory(0, slavio_led_mem_read, |
|
472 slavio_led_mem_write, s); |
|
473 /* ss600mp diag LEDs */ |
|
474 cpu_register_physical_memory(base + MISC_LEDS, MISC_SIZE, io); |
|
475 |
|
476 /* 32 bit registers */ |
|
477 io = cpu_register_io_memory(0, slavio_sysctrl_mem_read, |
|
478 slavio_sysctrl_mem_write, s); |
|
479 // System control |
|
480 cpu_register_physical_memory(base + MISC_SYS, SYSCTRL_SIZE, io); |
|
481 } |
|
482 |
|
483 // AUX 1 (Misc System Functions) |
|
484 if (aux1_base) { |
|
485 io = cpu_register_io_memory(0, slavio_aux1_mem_read, |
|
486 slavio_aux1_mem_write, s); |
|
487 cpu_register_physical_memory(aux1_base, MISC_SIZE, io); |
|
488 } |
|
489 |
|
490 // AUX 2 (Software Powerdown Control) |
|
491 if (aux2_base) { |
|
492 io = cpu_register_io_memory(0, slavio_aux2_mem_read, |
|
493 slavio_aux2_mem_write, s); |
|
494 cpu_register_physical_memory(aux2_base, MISC_SIZE, io); |
|
495 } |
|
496 |
|
497 // Power management (APC) XXX: not a Slavio device |
|
498 if (power_base) { |
|
499 io = cpu_register_io_memory(0, apc_mem_read, apc_mem_write, s); |
|
500 cpu_register_physical_memory(power_base, MISC_SIZE, io); |
|
501 } |
|
502 |
|
503 s->irq = irq; |
|
504 s->cpu_halt = cpu_halt; |
|
505 *fdc_tc = &s->fdc_tc; |
|
506 |
|
507 register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, |
|
508 s); |
|
509 qemu_register_reset(slavio_misc_reset, s); |
|
510 slavio_misc_reset(s); |
|
511 |
|
512 return s; |
|
513 } |