]>
Commit | Line | Data |
---|---|---|
ee76f82e BS |
1 | /* |
2 | * QEMU Sparc Sun4c interrupt controller emulation | |
3 | * | |
4 | * Based on slavio_intctl, copyright (c) 2003-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" | |
376253ec | 26 | #include "monitor.h" |
ee76f82e BS |
27 | //#define DEBUG_IRQ_COUNT |
28 | //#define DEBUG_IRQ | |
29 | ||
30 | #ifdef DEBUG_IRQ | |
001faf32 BS |
31 | #define DPRINTF(fmt, ...) \ |
32 | do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0) | |
ee76f82e | 33 | #else |
001faf32 | 34 | #define DPRINTF(fmt, ...) |
ee76f82e BS |
35 | #endif |
36 | ||
37 | /* | |
38 | * Registers of interrupt controller in sun4c. | |
39 | * | |
40 | */ | |
41 | ||
42 | #define MAX_PILS 16 | |
43 | ||
44 | typedef struct Sun4c_INTCTLState { | |
45 | #ifdef DEBUG_IRQ_COUNT | |
46 | uint64_t irq_count; | |
47 | #endif | |
48 | qemu_irq *cpu_irqs; | |
49 | const uint32_t *intbit_to_level; | |
50 | uint32_t pil_out; | |
51 | uint8_t reg; | |
52 | uint8_t pending; | |
53 | } Sun4c_INTCTLState; | |
54 | ||
e64d7d59 | 55 | #define INTCTL_SIZE 1 |
ee76f82e BS |
56 | |
57 | static void sun4c_check_interrupts(void *opaque); | |
58 | ||
59 | static uint32_t sun4c_intctl_mem_readb(void *opaque, target_phys_addr_t addr) | |
60 | { | |
61 | Sun4c_INTCTLState *s = opaque; | |
62 | uint32_t ret; | |
63 | ||
64 | ret = s->reg; | |
65 | DPRINTF("read reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); | |
66 | ||
67 | return ret; | |
68 | } | |
69 | ||
77f193da BS |
70 | static void sun4c_intctl_mem_writeb(void *opaque, target_phys_addr_t addr, |
71 | uint32_t val) | |
ee76f82e BS |
72 | { |
73 | Sun4c_INTCTLState *s = opaque; | |
74 | ||
75 | DPRINTF("write reg 0x" TARGET_FMT_plx " = %x\n", addr, val); | |
76 | val &= 0xbf; | |
77 | s->reg = val; | |
78 | sun4c_check_interrupts(s); | |
79 | } | |
80 | ||
81 | static CPUReadMemoryFunc *sun4c_intctl_mem_read[3] = { | |
82 | sun4c_intctl_mem_readb, | |
7c560456 BS |
83 | NULL, |
84 | NULL, | |
ee76f82e BS |
85 | }; |
86 | ||
87 | static CPUWriteMemoryFunc *sun4c_intctl_mem_write[3] = { | |
88 | sun4c_intctl_mem_writeb, | |
7c560456 BS |
89 | NULL, |
90 | NULL, | |
ee76f82e BS |
91 | }; |
92 | ||
376253ec | 93 | void sun4c_pic_info(Monitor *mon, void *opaque) |
ee76f82e BS |
94 | { |
95 | Sun4c_INTCTLState *s = opaque; | |
96 | ||
376253ec AL |
97 | monitor_printf(mon, "master: pending 0x%2.2x, enabled 0x%2.2x\n", |
98 | s->pending, s->reg); | |
ee76f82e BS |
99 | } |
100 | ||
376253ec | 101 | void sun4c_irq_info(Monitor *mon, void *opaque) |
ee76f82e BS |
102 | { |
103 | #ifndef DEBUG_IRQ_COUNT | |
376253ec | 104 | monitor_printf(mon, "irq statistic code not compiled.\n"); |
ee76f82e BS |
105 | #else |
106 | Sun4c_INTCTLState *s = opaque; | |
107 | int64_t count; | |
108 | ||
376253ec | 109 | monitor_printf(mon, "IRQ statistics:\n"); |
0bf9e31a | 110 | count = s->irq_count; |
ee76f82e | 111 | if (count > 0) |
0bf9e31a | 112 | monitor_printf(mon, " %" PRId64 "\n", count); |
ee76f82e BS |
113 | #endif |
114 | } | |
115 | ||
116 | static const uint32_t intbit_to_level[] = { 0, 1, 4, 6, 8, 10, 0, 14, }; | |
117 | ||
118 | static void sun4c_check_interrupts(void *opaque) | |
119 | { | |
120 | Sun4c_INTCTLState *s = opaque; | |
121 | uint32_t pil_pending; | |
122 | unsigned int i; | |
123 | ||
ee76f82e BS |
124 | pil_pending = 0; |
125 | if (s->pending && !(s->reg & 0x80000000)) { | |
126 | for (i = 0; i < 8; i++) { | |
127 | if (s->pending & (1 << i)) | |
128 | pil_pending |= 1 << intbit_to_level[i]; | |
129 | } | |
130 | } | |
131 | ||
132 | for (i = 0; i < MAX_PILS; i++) { | |
133 | if (pil_pending & (1 << i)) { | |
134 | if (!(s->pil_out & (1 << i))) | |
135 | qemu_irq_raise(s->cpu_irqs[i]); | |
136 | } else { | |
137 | if (s->pil_out & (1 << i)) | |
138 | qemu_irq_lower(s->cpu_irqs[i]); | |
139 | } | |
140 | } | |
141 | s->pil_out = pil_pending; | |
142 | } | |
143 | ||
144 | /* | |
145 | * "irq" here is the bit number in the system interrupt register | |
146 | */ | |
147 | static void sun4c_set_irq(void *opaque, int irq, int level) | |
148 | { | |
149 | Sun4c_INTCTLState *s = opaque; | |
150 | uint32_t mask = 1 << irq; | |
151 | uint32_t pil = intbit_to_level[irq]; | |
152 | ||
153 | DPRINTF("Set irq %d -> pil %d level %d\n", irq, pil, | |
154 | level); | |
155 | if (pil > 0) { | |
156 | if (level) { | |
157 | #ifdef DEBUG_IRQ_COUNT | |
0bf9e31a | 158 | s->irq_count++; |
ee76f82e BS |
159 | #endif |
160 | s->pending |= mask; | |
161 | } else { | |
162 | s->pending &= ~mask; | |
163 | } | |
164 | sun4c_check_interrupts(s); | |
165 | } | |
166 | } | |
167 | ||
168 | static void sun4c_intctl_save(QEMUFile *f, void *opaque) | |
169 | { | |
170 | Sun4c_INTCTLState *s = opaque; | |
171 | ||
172 | qemu_put_8s(f, &s->reg); | |
173 | qemu_put_8s(f, &s->pending); | |
174 | } | |
175 | ||
176 | static int sun4c_intctl_load(QEMUFile *f, void *opaque, int version_id) | |
177 | { | |
178 | Sun4c_INTCTLState *s = opaque; | |
179 | ||
180 | if (version_id != 1) | |
181 | return -EINVAL; | |
182 | ||
183 | qemu_get_8s(f, &s->reg); | |
184 | qemu_get_8s(f, &s->pending); | |
ee76f82e BS |
185 | |
186 | return 0; | |
187 | } | |
188 | ||
189 | static void sun4c_intctl_reset(void *opaque) | |
190 | { | |
191 | Sun4c_INTCTLState *s = opaque; | |
192 | ||
193 | s->reg = 1; | |
194 | s->pending = 0; | |
ee76f82e BS |
195 | } |
196 | ||
197 | void *sun4c_intctl_init(target_phys_addr_t addr, qemu_irq **irq, | |
198 | qemu_irq *parent_irq) | |
199 | { | |
200 | int sun4c_intctl_io_memory; | |
201 | Sun4c_INTCTLState *s; | |
202 | ||
203 | s = qemu_mallocz(sizeof(Sun4c_INTCTLState)); | |
ee76f82e | 204 | |
1eed09cb | 205 | sun4c_intctl_io_memory = cpu_register_io_memory(sun4c_intctl_mem_read, |
ee76f82e BS |
206 | sun4c_intctl_mem_write, s); |
207 | cpu_register_physical_memory(addr, INTCTL_SIZE, sun4c_intctl_io_memory); | |
208 | s->cpu_irqs = parent_irq; | |
209 | ||
210 | register_savevm("sun4c_intctl", addr, 1, sun4c_intctl_save, | |
211 | sun4c_intctl_load, s); | |
212 | ||
a08d4367 | 213 | qemu_register_reset(sun4c_intctl_reset, s); |
ee76f82e BS |
214 | *irq = qemu_allocate_irqs(sun4c_set_irq, s, 8); |
215 | ||
216 | sun4c_intctl_reset(s); | |
217 | return s; | |
218 | } |