Unify IRQ handling.
[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 #include "vl.h"
25 /* debug misc */
26 //#define DEBUG_MISC
27
28 /*
29  * This is the auxio port, chip control and system control part of
30  * chip STP2001 (Slave I/O), also produced as NCR89C105. See
31  * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
32  *
33  * This also includes the PMC CPU idle controller.
34  */
35
36 #ifdef DEBUG_MISC
37 #define MISC_DPRINTF(fmt, args...) \
38 do { printf("MISC: " fmt , ##args); } while (0)
39 #else
40 #define MISC_DPRINTF(fmt, args...)
41 #endif
42
43 typedef struct MiscState {
44     qemu_irq irq;
45     uint8_t config;
46     uint8_t aux1, aux2;
47     uint8_t diag, mctrl, sysctrl;
48 } MiscState;
49
50 #define MISC_MAXADDR 1
51
52 static void slavio_misc_update_irq(void *opaque)
53 {
54     MiscState *s = opaque;
55
56     if ((s->aux2 & 0x4) && (s->config & 0x8)) {
57         MISC_DPRINTF("Raise IRQ\n");
58         qemu_irq_raise(s->irq);
59     } else {
60         MISC_DPRINTF("Lower IRQ\n");
61         qemu_irq_lower(s->irq);
62     }
63 }
64
65 static void slavio_misc_reset(void *opaque)
66 {
67     MiscState *s = opaque;
68
69     // Diagnostic and system control registers not cleared in reset
70     s->config = s->aux1 = s->aux2 = s->mctrl = 0;
71 }
72
73 void slavio_set_power_fail(void *opaque, int power_failing)
74 {
75     MiscState *s = opaque;
76
77     MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config);
78     if (power_failing && (s->config & 0x8)) {
79         s->aux2 |= 0x4;
80     } else {
81         s->aux2 &= ~0x4;
82     }
83     slavio_misc_update_irq(s);
84 }
85
86 static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
87 {
88     MiscState *s = opaque;
89
90     switch (addr & 0xfff0000) {
91     case 0x1800000:
92         MISC_DPRINTF("Write config %2.2x\n", val & 0xff);
93         s->config = val & 0xff;
94         slavio_misc_update_irq(s);
95         break;
96     case 0x1900000:
97         MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
98         s->aux1 = val & 0xff;
99         break;
100     case 0x1910000:
101         val &= 0x3;
102         MISC_DPRINTF("Write aux2 %2.2x\n", val);
103         val |= s->aux2 & 0x4;
104         if (val & 0x2) // Clear Power Fail int
105             val &= 0x1;
106         s->aux2 = val;
107         if (val & 1)
108             qemu_system_shutdown_request();
109         slavio_misc_update_irq(s);
110         break;
111     case 0x1a00000:
112         MISC_DPRINTF("Write diag %2.2x\n", val & 0xff);
113         s->diag = val & 0xff;
114         break;
115     case 0x1b00000:
116         MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff);
117         s->mctrl = val & 0xff;
118         break;
119     case 0x1f00000:
120         MISC_DPRINTF("Write system control %2.2x\n", val & 0xff);
121         if (val & 1) {
122             s->sysctrl = 0x2;
123             qemu_system_reset_request();
124         }
125         break;
126     case 0xa000000:
127         MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
128         cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
129         break;
130     }
131 }
132
133 static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
134 {
135     MiscState *s = opaque;
136     uint32_t ret = 0;
137
138     switch (addr & 0xfff0000) {
139     case 0x1800000:
140         ret = s->config;
141         MISC_DPRINTF("Read config %2.2x\n", ret);
142         break;
143     case 0x1900000:
144         ret = s->aux1;
145         MISC_DPRINTF("Read aux1 %2.2x\n", ret);
146         break;
147     case 0x1910000:
148         ret = s->aux2;
149         MISC_DPRINTF("Read aux2 %2.2x\n", ret);
150         break;
151     case 0x1a00000:
152         ret = s->diag;
153         MISC_DPRINTF("Read diag %2.2x\n", ret);
154         break;
155     case 0x1b00000:
156         ret = s->mctrl;
157         MISC_DPRINTF("Read modem control %2.2x\n", ret);
158         break;
159     case 0x1f00000:
160         MISC_DPRINTF("Read system control %2.2x\n", ret);
161         ret = s->sysctrl;
162         break;
163     case 0xa000000:
164         MISC_DPRINTF("Read power management %2.2x\n", ret);
165         break;
166     }
167     return ret;
168 }
169
170 static CPUReadMemoryFunc *slavio_misc_mem_read[3] = {
171     slavio_misc_mem_readb,
172     slavio_misc_mem_readb,
173     slavio_misc_mem_readb,
174 };
175
176 static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = {
177     slavio_misc_mem_writeb,
178     slavio_misc_mem_writeb,
179     slavio_misc_mem_writeb,
180 };
181
182 static void slavio_misc_save(QEMUFile *f, void *opaque)
183 {
184     MiscState *s = opaque;
185     int tmp;
186
187     tmp = 0;
188     qemu_put_be32s(f, &tmp); /* ignored, was IRQ.  */
189     qemu_put_8s(f, &s->config);
190     qemu_put_8s(f, &s->aux1);
191     qemu_put_8s(f, &s->aux2);
192     qemu_put_8s(f, &s->diag);
193     qemu_put_8s(f, &s->mctrl);
194     qemu_put_8s(f, &s->sysctrl);
195 }
196
197 static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id)
198 {
199     MiscState *s = opaque;
200     int tmp;
201
202     if (version_id != 1)
203         return -EINVAL;
204
205     qemu_get_be32s(f, &tmp);
206     qemu_get_8s(f, &s->config);
207     qemu_get_8s(f, &s->aux1);
208     qemu_get_8s(f, &s->aux2);
209     qemu_get_8s(f, &s->diag);
210     qemu_get_8s(f, &s->mctrl);
211     qemu_get_8s(f, &s->sysctrl);
212     return 0;
213 }
214
215 void *slavio_misc_init(uint32_t base, qemu_irq irq)
216 {
217     int slavio_misc_io_memory;
218     MiscState *s;
219
220     s = qemu_mallocz(sizeof(MiscState));
221     if (!s)
222         return NULL;
223
224     slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read, slavio_misc_mem_write, s);
225     // Slavio control
226     cpu_register_physical_memory(base + 0x1800000, MISC_MAXADDR, slavio_misc_io_memory);
227     // AUX 1
228     cpu_register_physical_memory(base + 0x1900000, MISC_MAXADDR, slavio_misc_io_memory);
229     // AUX 2
230     cpu_register_physical_memory(base + 0x1910000, MISC_MAXADDR, slavio_misc_io_memory);
231     // Diagnostics
232     cpu_register_physical_memory(base + 0x1a00000, MISC_MAXADDR, slavio_misc_io_memory);
233     // Modem control
234     cpu_register_physical_memory(base + 0x1b00000, MISC_MAXADDR, slavio_misc_io_memory);
235     // System control
236     cpu_register_physical_memory(base + 0x1f00000, MISC_MAXADDR, slavio_misc_io_memory);
237     // Power management
238     cpu_register_physical_memory(base + 0xa000000, MISC_MAXADDR, slavio_misc_io_memory);
239
240     s->irq = irq;
241
242     register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, s);
243     qemu_register_reset(slavio_misc_reset, s);
244     slavio_misc_reset(s);
245     return s;
246 }