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