]>
Commit | Line | Data |
---|---|---|
b5cec4c5 DG |
1 | /* |
2 | * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator | |
3 | * | |
4 | * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics | |
5 | * | |
6 | * Copyright (c) 2010,2011 David Gibson, IBM Corporation. | |
7 | * | |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
9 | * of this software and associated documentation files (the "Software"), to deal | |
10 | * in the Software without restriction, including without limitation the rights | |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
12 | * copies of the Software, and to permit persons to whom the Software is | |
13 | * furnished to do so, subject to the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
24 | * THE SOFTWARE. | |
25 | * | |
26 | */ | |
27 | ||
0d75590d | 28 | #include "qemu/osdep.h" |
da34e65c | 29 | #include "qapi/error.h" |
4771d756 PB |
30 | #include "qemu-common.h" |
31 | #include "cpu.h" | |
83c9f4ca | 32 | #include "hw/hw.h" |
500efa23 | 33 | #include "trace.h" |
5d87e4b7 | 34 | #include "qemu/timer.h" |
0d09e41a | 35 | #include "hw/ppc/xics.h" |
9ccff2a4 | 36 | #include "qemu/error-report.h" |
5a3d7b23 | 37 | #include "qapi/visitor.h" |
b1fc72f0 BH |
38 | #include "monitor/monitor.h" |
39 | #include "hw/intc/intc.h" | |
b5cec4c5 | 40 | |
9c7027ba | 41 | int xics_get_cpu_index_by_dt_id(int cpu_dt_id) |
0f20ba62 AK |
42 | { |
43 | PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); | |
44 | ||
45 | if (cpu) { | |
46 | return cpu->parent_obj.cpu_index; | |
47 | } | |
48 | ||
49 | return -1; | |
50 | } | |
51 | ||
b4f27d71 | 52 | void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) |
4a4b344c BR |
53 | { |
54 | CPUState *cs = CPU(cpu); | |
b4f27d71 | 55 | ICPState *ss = xics_icp_get(xi, cs->cpu_index); |
4a4b344c | 56 | |
b4f27d71 | 57 | assert(ss); |
4a4b344c BR |
58 | assert(cs == ss->cs); |
59 | ||
60 | ss->output = NULL; | |
61 | ss->cs = NULL; | |
62 | } | |
63 | ||
b4f27d71 | 64 | void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu) |
8ffe04ed AK |
65 | { |
66 | CPUState *cs = CPU(cpu); | |
67 | CPUPPCState *env = &cpu->env; | |
b4f27d71 | 68 | ICPState *ss = xics_icp_get(xi, cs->cpu_index); |
f0232434 | 69 | ICPStateClass *icpc; |
8ffe04ed | 70 | |
b4f27d71 | 71 | assert(ss); |
8ffe04ed | 72 | |
4a4b344c BR |
73 | ss->cs = cs; |
74 | ||
f0232434 CLG |
75 | icpc = ICP_GET_CLASS(ss); |
76 | if (icpc->cpu_setup) { | |
77 | icpc->cpu_setup(ss, cpu); | |
5eb92ccc AK |
78 | } |
79 | ||
8ffe04ed AK |
80 | switch (PPC_INPUT(env)) { |
81 | case PPC_FLAGS_INPUT_POWER7: | |
82 | ss->output = env->irq_inputs[POWER7_INPUT_INT]; | |
83 | break; | |
84 | ||
85 | case PPC_FLAGS_INPUT_970: | |
86 | ss->output = env->irq_inputs[PPC970_INPUT_INT]; | |
87 | break; | |
88 | ||
89 | default: | |
9ccff2a4 AK |
90 | error_report("XICS interrupt controller does not support this CPU " |
91 | "bus model"); | |
8ffe04ed AK |
92 | abort(); |
93 | } | |
94 | } | |
95 | ||
b9038e78 CLG |
96 | static void icp_pic_print_info(InterruptStatsProvider *obj, |
97 | Monitor *mon) | |
b1fc72f0 | 98 | { |
b9038e78 CLG |
99 | ICPState *icp = ICP(obj); |
100 | int cpu_index = icp->cs ? icp->cs->cpu_index : -1; | |
101 | ||
102 | if (!icp->output) { | |
103 | return; | |
104 | } | |
105 | monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", | |
106 | cpu_index, icp->xirr, icp->xirr_owner, | |
107 | icp->pending_priority, icp->mfrr); | |
108 | } | |
109 | ||
110 | static void ics_simple_pic_print_info(InterruptStatsProvider *obj, | |
111 | Monitor *mon) | |
112 | { | |
113 | ICSState *ics = ICS_SIMPLE(obj); | |
b1fc72f0 BH |
114 | uint32_t i; |
115 | ||
b9038e78 CLG |
116 | monitor_printf(mon, "ICS %4x..%4x %p\n", |
117 | ics->offset, ics->offset + ics->nr_irqs - 1, ics); | |
b1fc72f0 | 118 | |
b9038e78 CLG |
119 | if (!ics->irqs) { |
120 | return; | |
b1fc72f0 BH |
121 | } |
122 | ||
b9038e78 CLG |
123 | for (i = 0; i < ics->nr_irqs; i++) { |
124 | ICSIRQState *irq = ics->irqs + i; | |
b1fc72f0 | 125 | |
b9038e78 | 126 | if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { |
b1fc72f0 BH |
127 | continue; |
128 | } | |
b9038e78 CLG |
129 | monitor_printf(mon, " %4x %s %02x %02x\n", |
130 | ics->offset + i, | |
131 | (irq->flags & XICS_FLAGS_IRQ_LSI) ? | |
132 | "LSI" : "MSI", | |
133 | irq->priority, irq->status); | |
b1fc72f0 BH |
134 | } |
135 | } | |
136 | ||
b5cec4c5 DG |
137 | /* |
138 | * ICP: Presentation layer | |
139 | */ | |
140 | ||
b5cec4c5 DG |
141 | #define XISR_MASK 0x00ffffff |
142 | #define CPPR_MASK 0xff000000 | |
143 | ||
144 | #define XISR(ss) (((ss)->xirr) & XISR_MASK) | |
145 | #define CPPR(ss) (((ss)->xirr) >> 24) | |
146 | ||
d4d7a59a BH |
147 | static void ics_reject(ICSState *ics, uint32_t nr) |
148 | { | |
149 | ICSStateClass *k = ICS_BASE_GET_CLASS(ics); | |
150 | ||
151 | if (k->reject) { | |
152 | k->reject(ics, nr); | |
153 | } | |
154 | } | |
155 | ||
7844e12b | 156 | void ics_resend(ICSState *ics) |
d4d7a59a BH |
157 | { |
158 | ICSStateClass *k = ICS_BASE_GET_CLASS(ics); | |
159 | ||
160 | if (k->resend) { | |
161 | k->resend(ics); | |
162 | } | |
163 | } | |
164 | ||
165 | static void ics_eoi(ICSState *ics, int nr) | |
166 | { | |
167 | ICSStateClass *k = ICS_BASE_GET_CLASS(ics); | |
168 | ||
169 | if (k->eoi) { | |
170 | k->eoi(ics, nr); | |
171 | } | |
172 | } | |
b5cec4c5 | 173 | |
cc706a53 | 174 | static void icp_check_ipi(ICPState *ss) |
b5cec4c5 | 175 | { |
b5cec4c5 DG |
176 | if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { |
177 | return; | |
178 | } | |
179 | ||
cc706a53 | 180 | trace_xics_icp_check_ipi(ss->cs->cpu_index, ss->mfrr); |
500efa23 | 181 | |
cc706a53 BH |
182 | if (XISR(ss) && ss->xirr_owner) { |
183 | ics_reject(ss->xirr_owner, XISR(ss)); | |
b5cec4c5 DG |
184 | } |
185 | ||
186 | ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; | |
187 | ss->pending_priority = ss->mfrr; | |
cc706a53 | 188 | ss->xirr_owner = NULL; |
b5cec4c5 DG |
189 | qemu_irq_raise(ss->output); |
190 | } | |
191 | ||
b2fc59aa | 192 | void icp_resend(ICPState *ss) |
b5cec4c5 | 193 | { |
2cd908d0 CLG |
194 | XICSFabric *xi = ss->xics; |
195 | XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi); | |
b5cec4c5 DG |
196 | |
197 | if (ss->mfrr < CPPR(ss)) { | |
cc706a53 BH |
198 | icp_check_ipi(ss); |
199 | } | |
2cd908d0 CLG |
200 | |
201 | xic->ics_resend(xi); | |
b5cec4c5 DG |
202 | } |
203 | ||
e3403258 | 204 | void icp_set_cppr(ICPState *ss, uint8_t cppr) |
b5cec4c5 | 205 | { |
b5cec4c5 DG |
206 | uint8_t old_cppr; |
207 | uint32_t old_xisr; | |
208 | ||
209 | old_cppr = CPPR(ss); | |
210 | ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); | |
211 | ||
212 | if (cppr < old_cppr) { | |
213 | if (XISR(ss) && (cppr <= ss->pending_priority)) { | |
214 | old_xisr = XISR(ss); | |
215 | ss->xirr &= ~XISR_MASK; /* Clear XISR */ | |
e03c902c | 216 | ss->pending_priority = 0xff; |
b5cec4c5 | 217 | qemu_irq_lower(ss->output); |
cc706a53 BH |
218 | if (ss->xirr_owner) { |
219 | ics_reject(ss->xirr_owner, old_xisr); | |
220 | ss->xirr_owner = NULL; | |
221 | } | |
b5cec4c5 DG |
222 | } |
223 | } else { | |
224 | if (!XISR(ss)) { | |
e3403258 | 225 | icp_resend(ss); |
b5cec4c5 DG |
226 | } |
227 | } | |
228 | } | |
229 | ||
e3403258 | 230 | void icp_set_mfrr(ICPState *ss, uint8_t mfrr) |
b5cec4c5 | 231 | { |
b5cec4c5 DG |
232 | ss->mfrr = mfrr; |
233 | if (mfrr < CPPR(ss)) { | |
cc706a53 | 234 | icp_check_ipi(ss); |
b5cec4c5 DG |
235 | } |
236 | } | |
237 | ||
9c7027ba | 238 | uint32_t icp_accept(ICPState *ss) |
b5cec4c5 | 239 | { |
500efa23 | 240 | uint32_t xirr = ss->xirr; |
b5cec4c5 DG |
241 | |
242 | qemu_irq_lower(ss->output); | |
b5cec4c5 | 243 | ss->xirr = ss->pending_priority << 24; |
e03c902c | 244 | ss->pending_priority = 0xff; |
cc706a53 | 245 | ss->xirr_owner = NULL; |
500efa23 DG |
246 | |
247 | trace_xics_icp_accept(xirr, ss->xirr); | |
248 | ||
b5cec4c5 DG |
249 | return xirr; |
250 | } | |
251 | ||
1cbd2220 BH |
252 | uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr) |
253 | { | |
254 | if (mfrr) { | |
255 | *mfrr = ss->mfrr; | |
256 | } | |
257 | return ss->xirr; | |
258 | } | |
259 | ||
e3403258 | 260 | void icp_eoi(ICPState *ss, uint32_t xirr) |
b5cec4c5 | 261 | { |
2cd908d0 CLG |
262 | XICSFabric *xi = ss->xics; |
263 | XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi); | |
cc706a53 BH |
264 | ICSState *ics; |
265 | uint32_t irq; | |
b5cec4c5 | 266 | |
b5cec4c5 DG |
267 | /* Send EOI -> ICS */ |
268 | ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); | |
e3403258 | 269 | trace_xics_icp_eoi(ss->cs->cpu_index, xirr, ss->xirr); |
cc706a53 | 270 | irq = xirr & XISR_MASK; |
2cd908d0 CLG |
271 | |
272 | ics = xic->ics_get(xi, irq); | |
273 | if (ics) { | |
274 | ics_eoi(ics, irq); | |
cc706a53 | 275 | } |
b5cec4c5 | 276 | if (!XISR(ss)) { |
e3403258 | 277 | icp_resend(ss); |
b5cec4c5 DG |
278 | } |
279 | } | |
280 | ||
cc706a53 | 281 | static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) |
b5cec4c5 | 282 | { |
b4f27d71 | 283 | ICPState *ss = xics_icp_get(ics->xics, server); |
b5cec4c5 | 284 | |
500efa23 DG |
285 | trace_xics_icp_irq(server, nr, priority); |
286 | ||
b5cec4c5 DG |
287 | if ((priority >= CPPR(ss)) |
288 | || (XISR(ss) && (ss->pending_priority <= priority))) { | |
cc706a53 | 289 | ics_reject(ics, nr); |
b5cec4c5 | 290 | } else { |
cc706a53 BH |
291 | if (XISR(ss) && ss->xirr_owner) { |
292 | ics_reject(ss->xirr_owner, XISR(ss)); | |
293 | ss->xirr_owner = NULL; | |
b5cec4c5 DG |
294 | } |
295 | ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); | |
cc706a53 | 296 | ss->xirr_owner = ics; |
b5cec4c5 | 297 | ss->pending_priority = priority; |
500efa23 | 298 | trace_xics_icp_raise(ss->xirr, ss->pending_priority); |
b5cec4c5 DG |
299 | qemu_irq_raise(ss->output); |
300 | } | |
301 | } | |
302 | ||
d1b5682d AK |
303 | static void icp_dispatch_pre_save(void *opaque) |
304 | { | |
305 | ICPState *ss = opaque; | |
306 | ICPStateClass *info = ICP_GET_CLASS(ss); | |
307 | ||
308 | if (info->pre_save) { | |
309 | info->pre_save(ss); | |
310 | } | |
311 | } | |
312 | ||
313 | static int icp_dispatch_post_load(void *opaque, int version_id) | |
314 | { | |
315 | ICPState *ss = opaque; | |
316 | ICPStateClass *info = ICP_GET_CLASS(ss); | |
317 | ||
318 | if (info->post_load) { | |
319 | return info->post_load(ss, version_id); | |
320 | } | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
c04d6cfa AL |
325 | static const VMStateDescription vmstate_icp_server = { |
326 | .name = "icp/server", | |
327 | .version_id = 1, | |
328 | .minimum_version_id = 1, | |
d1b5682d AK |
329 | .pre_save = icp_dispatch_pre_save, |
330 | .post_load = icp_dispatch_post_load, | |
3aff6c2f | 331 | .fields = (VMStateField[]) { |
c04d6cfa AL |
332 | /* Sanity check */ |
333 | VMSTATE_UINT32(xirr, ICPState), | |
334 | VMSTATE_UINT8(pending_priority, ICPState), | |
335 | VMSTATE_UINT8(mfrr, ICPState), | |
336 | VMSTATE_END_OF_LIST() | |
337 | }, | |
b5cec4c5 DG |
338 | }; |
339 | ||
c04d6cfa AL |
340 | static void icp_reset(DeviceState *dev) |
341 | { | |
342 | ICPState *icp = ICP(dev); | |
343 | ||
344 | icp->xirr = 0; | |
345 | icp->pending_priority = 0xff; | |
346 | icp->mfrr = 0xff; | |
347 | ||
348 | /* Make all outputs are deasserted */ | |
349 | qemu_set_irq(icp->output, 0); | |
350 | } | |
351 | ||
817bb6a4 CLG |
352 | static void icp_realize(DeviceState *dev, Error **errp) |
353 | { | |
354 | ICPState *icp = ICP(dev); | |
355 | Object *obj; | |
356 | Error *err = NULL; | |
357 | ||
358 | obj = object_property_get_link(OBJECT(dev), "xics", &err); | |
359 | if (!obj) { | |
360 | error_setg(errp, "%s: required link 'xics' not found: %s", | |
361 | __func__, error_get_pretty(err)); | |
362 | return; | |
363 | } | |
364 | ||
2cd908d0 | 365 | icp->xics = XICS_FABRIC(obj); |
817bb6a4 CLG |
366 | } |
367 | ||
368 | ||
c04d6cfa AL |
369 | static void icp_class_init(ObjectClass *klass, void *data) |
370 | { | |
371 | DeviceClass *dc = DEVICE_CLASS(klass); | |
b9038e78 | 372 | InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); |
c04d6cfa AL |
373 | |
374 | dc->reset = icp_reset; | |
375 | dc->vmsd = &vmstate_icp_server; | |
817bb6a4 | 376 | dc->realize = icp_realize; |
b9038e78 | 377 | ic->print_info = icp_pic_print_info; |
c04d6cfa AL |
378 | } |
379 | ||
456df19c | 380 | static const TypeInfo icp_info = { |
c04d6cfa AL |
381 | .name = TYPE_ICP, |
382 | .parent = TYPE_DEVICE, | |
383 | .instance_size = sizeof(ICPState), | |
384 | .class_init = icp_class_init, | |
d1b5682d | 385 | .class_size = sizeof(ICPStateClass), |
b9038e78 CLG |
386 | .interfaces = (InterfaceInfo[]) { |
387 | { TYPE_INTERRUPT_STATS_PROVIDER }, | |
388 | { } | |
389 | }, | |
b5cec4c5 DG |
390 | }; |
391 | ||
c04d6cfa AL |
392 | /* |
393 | * ICS: Source layer | |
394 | */ | |
d4d7a59a | 395 | static void ics_simple_resend_msi(ICSState *ics, int srcno) |
d07fee7e | 396 | { |
c04d6cfa | 397 | ICSIRQState *irq = ics->irqs + srcno; |
d07fee7e DG |
398 | |
399 | /* FIXME: filter by server#? */ | |
98ca8c02 DG |
400 | if (irq->status & XICS_STATUS_REJECTED) { |
401 | irq->status &= ~XICS_STATUS_REJECTED; | |
d07fee7e | 402 | if (irq->priority != 0xff) { |
cc706a53 | 403 | icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); |
d07fee7e DG |
404 | } |
405 | } | |
406 | } | |
407 | ||
d4d7a59a | 408 | static void ics_simple_resend_lsi(ICSState *ics, int srcno) |
d07fee7e | 409 | { |
c04d6cfa | 410 | ICSIRQState *irq = ics->irqs + srcno; |
d07fee7e | 411 | |
98ca8c02 DG |
412 | if ((irq->priority != 0xff) |
413 | && (irq->status & XICS_STATUS_ASSERTED) | |
414 | && !(irq->status & XICS_STATUS_SENT)) { | |
415 | irq->status |= XICS_STATUS_SENT; | |
cc706a53 | 416 | icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); |
d07fee7e DG |
417 | } |
418 | } | |
419 | ||
d4d7a59a | 420 | static void ics_simple_set_irq_msi(ICSState *ics, int srcno, int val) |
b5cec4c5 | 421 | { |
c04d6cfa | 422 | ICSIRQState *irq = ics->irqs + srcno; |
b5cec4c5 | 423 | |
d4d7a59a | 424 | trace_xics_ics_simple_set_irq_msi(srcno, srcno + ics->offset); |
500efa23 | 425 | |
b5cec4c5 DG |
426 | if (val) { |
427 | if (irq->priority == 0xff) { | |
98ca8c02 | 428 | irq->status |= XICS_STATUS_MASKED_PENDING; |
500efa23 | 429 | trace_xics_masked_pending(); |
b5cec4c5 | 430 | } else { |
cc706a53 | 431 | icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); |
b5cec4c5 DG |
432 | } |
433 | } | |
434 | } | |
435 | ||
d4d7a59a | 436 | static void ics_simple_set_irq_lsi(ICSState *ics, int srcno, int val) |
b5cec4c5 | 437 | { |
c04d6cfa | 438 | ICSIRQState *irq = ics->irqs + srcno; |
b5cec4c5 | 439 | |
d4d7a59a | 440 | trace_xics_ics_simple_set_irq_lsi(srcno, srcno + ics->offset); |
98ca8c02 DG |
441 | if (val) { |
442 | irq->status |= XICS_STATUS_ASSERTED; | |
443 | } else { | |
444 | irq->status &= ~XICS_STATUS_ASSERTED; | |
445 | } | |
d4d7a59a | 446 | ics_simple_resend_lsi(ics, srcno); |
b5cec4c5 DG |
447 | } |
448 | ||
d4d7a59a | 449 | static void ics_simple_set_irq(void *opaque, int srcno, int val) |
b5cec4c5 | 450 | { |
c04d6cfa | 451 | ICSState *ics = (ICSState *)opaque; |
b5cec4c5 | 452 | |
4af88944 | 453 | if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { |
d4d7a59a | 454 | ics_simple_set_irq_lsi(ics, srcno, val); |
d07fee7e | 455 | } else { |
d4d7a59a | 456 | ics_simple_set_irq_msi(ics, srcno, val); |
d07fee7e DG |
457 | } |
458 | } | |
b5cec4c5 | 459 | |
d4d7a59a | 460 | static void ics_simple_write_xive_msi(ICSState *ics, int srcno) |
d07fee7e | 461 | { |
c04d6cfa | 462 | ICSIRQState *irq = ics->irqs + srcno; |
d07fee7e | 463 | |
98ca8c02 DG |
464 | if (!(irq->status & XICS_STATUS_MASKED_PENDING) |
465 | || (irq->priority == 0xff)) { | |
d07fee7e | 466 | return; |
b5cec4c5 | 467 | } |
d07fee7e | 468 | |
98ca8c02 | 469 | irq->status &= ~XICS_STATUS_MASKED_PENDING; |
cc706a53 | 470 | icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); |
b5cec4c5 DG |
471 | } |
472 | ||
d4d7a59a | 473 | static void ics_simple_write_xive_lsi(ICSState *ics, int srcno) |
b5cec4c5 | 474 | { |
d4d7a59a | 475 | ics_simple_resend_lsi(ics, srcno); |
d07fee7e DG |
476 | } |
477 | ||
d4d7a59a BH |
478 | void ics_simple_write_xive(ICSState *ics, int srcno, int server, |
479 | uint8_t priority, uint8_t saved_priority) | |
d07fee7e | 480 | { |
c04d6cfa | 481 | ICSIRQState *irq = ics->irqs + srcno; |
b5cec4c5 DG |
482 | |
483 | irq->server = server; | |
484 | irq->priority = priority; | |
3fe719f4 | 485 | irq->saved_priority = saved_priority; |
b5cec4c5 | 486 | |
d4d7a59a BH |
487 | trace_xics_ics_simple_write_xive(ics->offset + srcno, srcno, server, |
488 | priority); | |
500efa23 | 489 | |
4af88944 | 490 | if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { |
d4d7a59a | 491 | ics_simple_write_xive_lsi(ics, srcno); |
d07fee7e | 492 | } else { |
d4d7a59a | 493 | ics_simple_write_xive_msi(ics, srcno); |
b5cec4c5 | 494 | } |
b5cec4c5 DG |
495 | } |
496 | ||
d4d7a59a | 497 | static void ics_simple_reject(ICSState *ics, uint32_t nr) |
b5cec4c5 | 498 | { |
c04d6cfa | 499 | ICSIRQState *irq = ics->irqs + nr - ics->offset; |
d07fee7e | 500 | |
d4d7a59a | 501 | trace_xics_ics_simple_reject(nr, nr - ics->offset); |
056b9775 ND |
502 | if (irq->flags & XICS_FLAGS_IRQ_MSI) { |
503 | irq->status |= XICS_STATUS_REJECTED; | |
504 | } else if (irq->flags & XICS_FLAGS_IRQ_LSI) { | |
505 | irq->status &= ~XICS_STATUS_SENT; | |
506 | } | |
b5cec4c5 DG |
507 | } |
508 | ||
d4d7a59a | 509 | static void ics_simple_resend(ICSState *ics) |
b5cec4c5 | 510 | { |
d07fee7e DG |
511 | int i; |
512 | ||
513 | for (i = 0; i < ics->nr_irqs; i++) { | |
d07fee7e | 514 | /* FIXME: filter by server#? */ |
4af88944 | 515 | if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { |
d4d7a59a | 516 | ics_simple_resend_lsi(ics, i); |
d07fee7e | 517 | } else { |
d4d7a59a | 518 | ics_simple_resend_msi(ics, i); |
d07fee7e DG |
519 | } |
520 | } | |
b5cec4c5 DG |
521 | } |
522 | ||
d4d7a59a | 523 | static void ics_simple_eoi(ICSState *ics, uint32_t nr) |
b5cec4c5 | 524 | { |
d07fee7e | 525 | int srcno = nr - ics->offset; |
c04d6cfa | 526 | ICSIRQState *irq = ics->irqs + srcno; |
d07fee7e | 527 | |
d4d7a59a | 528 | trace_xics_ics_simple_eoi(nr); |
500efa23 | 529 | |
4af88944 | 530 | if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { |
98ca8c02 | 531 | irq->status &= ~XICS_STATUS_SENT; |
d07fee7e | 532 | } |
b5cec4c5 DG |
533 | } |
534 | ||
d4d7a59a | 535 | static void ics_simple_reset(DeviceState *dev) |
c04d6cfa | 536 | { |
d4d7a59a | 537 | ICSState *ics = ICS_SIMPLE(dev); |
c04d6cfa | 538 | int i; |
a7e519a8 AK |
539 | uint8_t flags[ics->nr_irqs]; |
540 | ||
541 | for (i = 0; i < ics->nr_irqs; i++) { | |
542 | flags[i] = ics->irqs[i].flags; | |
543 | } | |
c04d6cfa AL |
544 | |
545 | memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); | |
a7e519a8 | 546 | |
c04d6cfa AL |
547 | for (i = 0; i < ics->nr_irqs; i++) { |
548 | ics->irqs[i].priority = 0xff; | |
549 | ics->irqs[i].saved_priority = 0xff; | |
a7e519a8 | 550 | ics->irqs[i].flags = flags[i]; |
c04d6cfa AL |
551 | } |
552 | } | |
553 | ||
d4d7a59a | 554 | static void ics_simple_dispatch_pre_save(void *opaque) |
d1b5682d AK |
555 | { |
556 | ICSState *ics = opaque; | |
d4d7a59a | 557 | ICSStateClass *info = ICS_BASE_GET_CLASS(ics); |
d1b5682d AK |
558 | |
559 | if (info->pre_save) { | |
560 | info->pre_save(ics); | |
561 | } | |
562 | } | |
563 | ||
d4d7a59a | 564 | static int ics_simple_dispatch_post_load(void *opaque, int version_id) |
d1b5682d AK |
565 | { |
566 | ICSState *ics = opaque; | |
d4d7a59a | 567 | ICSStateClass *info = ICS_BASE_GET_CLASS(ics); |
d1b5682d AK |
568 | |
569 | if (info->post_load) { | |
570 | return info->post_load(ics, version_id); | |
571 | } | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
d4d7a59a | 576 | static const VMStateDescription vmstate_ics_simple_irq = { |
c04d6cfa | 577 | .name = "ics/irq", |
4af88944 | 578 | .version_id = 2, |
c04d6cfa | 579 | .minimum_version_id = 1, |
3aff6c2f | 580 | .fields = (VMStateField[]) { |
c04d6cfa AL |
581 | VMSTATE_UINT32(server, ICSIRQState), |
582 | VMSTATE_UINT8(priority, ICSIRQState), | |
583 | VMSTATE_UINT8(saved_priority, ICSIRQState), | |
584 | VMSTATE_UINT8(status, ICSIRQState), | |
4af88944 | 585 | VMSTATE_UINT8(flags, ICSIRQState), |
c04d6cfa AL |
586 | VMSTATE_END_OF_LIST() |
587 | }, | |
588 | }; | |
589 | ||
d4d7a59a | 590 | static const VMStateDescription vmstate_ics_simple = { |
c04d6cfa AL |
591 | .name = "ics", |
592 | .version_id = 1, | |
593 | .minimum_version_id = 1, | |
d4d7a59a BH |
594 | .pre_save = ics_simple_dispatch_pre_save, |
595 | .post_load = ics_simple_dispatch_post_load, | |
3aff6c2f | 596 | .fields = (VMStateField[]) { |
c04d6cfa AL |
597 | /* Sanity check */ |
598 | VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), | |
599 | ||
600 | VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, | |
d4d7a59a BH |
601 | vmstate_ics_simple_irq, |
602 | ICSIRQState), | |
c04d6cfa AL |
603 | VMSTATE_END_OF_LIST() |
604 | }, | |
605 | }; | |
606 | ||
d4d7a59a | 607 | static void ics_simple_initfn(Object *obj) |
5a3d7b23 | 608 | { |
d4d7a59a | 609 | ICSState *ics = ICS_SIMPLE(obj); |
5a3d7b23 AK |
610 | |
611 | ics->offset = XICS_IRQ_BASE; | |
612 | } | |
613 | ||
d4d7a59a | 614 | static void ics_simple_realize(DeviceState *dev, Error **errp) |
c04d6cfa | 615 | { |
d4d7a59a | 616 | ICSState *ics = ICS_SIMPLE(dev); |
c04d6cfa | 617 | |
b45ff2d9 AK |
618 | if (!ics->nr_irqs) { |
619 | error_setg(errp, "Number of interrupts needs to be greater 0"); | |
620 | return; | |
621 | } | |
c04d6cfa | 622 | ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); |
d4d7a59a | 623 | ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); |
c04d6cfa AL |
624 | } |
625 | ||
4e4169f7 CLG |
626 | static Property ics_simple_properties[] = { |
627 | DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), | |
628 | DEFINE_PROP_END_OF_LIST(), | |
629 | }; | |
630 | ||
d4d7a59a | 631 | static void ics_simple_class_init(ObjectClass *klass, void *data) |
c04d6cfa AL |
632 | { |
633 | DeviceClass *dc = DEVICE_CLASS(klass); | |
d4d7a59a | 634 | ICSStateClass *isc = ICS_BASE_CLASS(klass); |
b9038e78 | 635 | InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); |
c04d6cfa | 636 | |
4e4169f7 CLG |
637 | isc->realize = ics_simple_realize; |
638 | dc->props = ics_simple_properties; | |
d4d7a59a BH |
639 | dc->vmsd = &vmstate_ics_simple; |
640 | dc->reset = ics_simple_reset; | |
d4d7a59a BH |
641 | isc->reject = ics_simple_reject; |
642 | isc->resend = ics_simple_resend; | |
643 | isc->eoi = ics_simple_eoi; | |
b9038e78 | 644 | ic->print_info = ics_simple_pic_print_info; |
c04d6cfa AL |
645 | } |
646 | ||
d4d7a59a BH |
647 | static const TypeInfo ics_simple_info = { |
648 | .name = TYPE_ICS_SIMPLE, | |
649 | .parent = TYPE_ICS_BASE, | |
650 | .instance_size = sizeof(ICSState), | |
651 | .class_init = ics_simple_class_init, | |
652 | .class_size = sizeof(ICSStateClass), | |
653 | .instance_init = ics_simple_initfn, | |
b9038e78 CLG |
654 | .interfaces = (InterfaceInfo[]) { |
655 | { TYPE_INTERRUPT_STATS_PROVIDER }, | |
656 | { } | |
657 | }, | |
d4d7a59a BH |
658 | }; |
659 | ||
4e4169f7 CLG |
660 | static void ics_base_realize(DeviceState *dev, Error **errp) |
661 | { | |
662 | ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); | |
663 | ICSState *ics = ICS_BASE(dev); | |
664 | Object *obj; | |
665 | Error *err = NULL; | |
666 | ||
667 | obj = object_property_get_link(OBJECT(dev), "xics", &err); | |
668 | if (!obj) { | |
669 | error_setg(errp, "%s: required link 'xics' not found: %s", | |
670 | __func__, error_get_pretty(err)); | |
671 | return; | |
672 | } | |
b4f27d71 | 673 | ics->xics = XICS_FABRIC(obj); |
4e4169f7 CLG |
674 | |
675 | ||
676 | if (icsc->realize) { | |
677 | icsc->realize(dev, errp); | |
678 | } | |
679 | } | |
680 | ||
681 | static void ics_base_class_init(ObjectClass *klass, void *data) | |
682 | { | |
683 | DeviceClass *dc = DEVICE_CLASS(klass); | |
684 | ||
685 | dc->realize = ics_base_realize; | |
686 | } | |
687 | ||
d4d7a59a BH |
688 | static const TypeInfo ics_base_info = { |
689 | .name = TYPE_ICS_BASE, | |
c04d6cfa | 690 | .parent = TYPE_DEVICE, |
d4d7a59a | 691 | .abstract = true, |
c04d6cfa | 692 | .instance_size = sizeof(ICSState), |
4e4169f7 | 693 | .class_init = ics_base_class_init, |
d1b5682d | 694 | .class_size = sizeof(ICSStateClass), |
c04d6cfa AL |
695 | }; |
696 | ||
51b18005 CLG |
697 | static const TypeInfo xics_fabric_info = { |
698 | .name = TYPE_XICS_FABRIC, | |
699 | .parent = TYPE_INTERFACE, | |
700 | .class_size = sizeof(XICSFabricClass), | |
701 | }; | |
702 | ||
b5cec4c5 DG |
703 | /* |
704 | * Exported functions | |
705 | */ | |
f7759e43 | 706 | qemu_irq xics_get_qirq(XICSFabric *xi, int irq) |
b5cec4c5 | 707 | { |
f7759e43 CLG |
708 | XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi); |
709 | ICSState *ics = xic->ics_get(xi, irq); | |
641c3493 | 710 | |
cc706a53 | 711 | if (ics) { |
641c3493 | 712 | return ics->qirqs[irq - ics->offset]; |
b5cec4c5 DG |
713 | } |
714 | ||
641c3493 | 715 | return NULL; |
a307d594 AK |
716 | } |
717 | ||
b4f27d71 CLG |
718 | ICPState *xics_icp_get(XICSFabric *xi, int server) |
719 | { | |
720 | XICSFabricClass *xic = XICS_FABRIC_GET_CLASS(xi); | |
721 | ||
722 | return xic->icp_get(xi, server); | |
723 | } | |
724 | ||
9c7027ba | 725 | void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) |
4af88944 AK |
726 | { |
727 | assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK)); | |
728 | ||
729 | ics->irqs[srcno].flags |= | |
730 | lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI; | |
731 | } | |
732 | ||
c04d6cfa AL |
733 | static void xics_register_types(void) |
734 | { | |
d4d7a59a BH |
735 | type_register_static(&ics_simple_info); |
736 | type_register_static(&ics_base_info); | |
c04d6cfa | 737 | type_register_static(&icp_info); |
51b18005 | 738 | type_register_static(&xics_fabric_info); |
b5cec4c5 | 739 | } |
c04d6cfa AL |
740 | |
741 | type_init(xics_register_types) |