]> git.proxmox.com Git - qemu.git/blame - hw/openpic.c
openpic: add Shared MSI support
[qemu.git] / hw / openpic.c
CommitLineData
dbda808a
FB
1/*
2 * OpenPIC emulation
5fafdf24 3 *
dbda808a 4 * Copyright (c) 2004 Jocelyn Mayer
704c7e5d 5 * 2011 Alexander Graf
5fafdf24 6 *
dbda808a
FB
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25/*
26 *
27 * Based on OpenPic implementations:
67b55785 28 * - Intel GW80314 I/O companion chip developer's manual
dbda808a
FB
29 * - Motorola MPC8245 & MPC8540 user manuals.
30 * - Motorola MCP750 (aka Raven) programmer manual.
31 * - Motorola Harrier programmer manuel
32 *
33 * Serial interrupts, as implemented in Raven chipset are not supported yet.
5fafdf24 34 *
dbda808a 35 */
87ecb68b
PB
36#include "hw.h"
37#include "ppc_mac.h"
38#include "pci.h"
b7169916 39#include "openpic.h"
d0b72631 40#include "sysbus.h"
732aa6ec 41#include "msi.h"
dbda808a 42
611493d9 43//#define DEBUG_OPENPIC
dbda808a
FB
44
45#ifdef DEBUG_OPENPIC
001faf32 46#define DPRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
dbda808a 47#else
001faf32 48#define DPRINTF(fmt, ...) do { } while (0)
dbda808a 49#endif
dbda808a 50
cdbb912a
AG
51#define MAX_CPU 15
52#define MAX_SRC 256
dbda808a
FB
53#define MAX_TMR 4
54#define VECTOR_BITS 8
55#define MAX_IPI 4
732aa6ec 56#define MAX_MSI 8
cdbb912a 57#define MAX_IRQ (MAX_SRC + MAX_IPI + MAX_TMR)
dbda808a 58#define VID 0x03 /* MPIC version ID */
dbda808a 59
d0b72631
AG
60/* OpenPIC capability flags */
61#define OPENPIC_FLAG_IDE_CRIT (1 << 0)
dbda808a 62
d0b72631 63/* OpenPIC address map */
780d16b7
AG
64#define OPENPIC_GLB_REG_START 0x0
65#define OPENPIC_GLB_REG_SIZE 0x10F0
66#define OPENPIC_TMR_REG_START 0x10F0
67#define OPENPIC_TMR_REG_SIZE 0x220
732aa6ec
AG
68#define OPENPIC_MSI_REG_START 0x1600
69#define OPENPIC_MSI_REG_SIZE 0x200
780d16b7
AG
70#define OPENPIC_SRC_REG_START 0x10000
71#define OPENPIC_SRC_REG_SIZE (MAX_SRC * 0x20)
72#define OPENPIC_CPU_REG_START 0x20000
73#define OPENPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000)
74
d0b72631
AG
75/* Raven */
76#define RAVEN_MAX_CPU 2
77#define RAVEN_MAX_EXT 48
78#define RAVEN_MAX_IRQ 64
79#define RAVEN_MAX_TMR MAX_TMR
80#define RAVEN_MAX_IPI MAX_IPI
81
82/* Interrupt definitions */
83#define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */
84#define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */
85#define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */
86#define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */
87/* First doorbell IRQ */
88#define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI))
89
90/* FSL_MPIC_20 */
91#define FSL_MPIC_20_MAX_CPU 1
92#define FSL_MPIC_20_MAX_EXT 12
93#define FSL_MPIC_20_MAX_INT 64
94#define FSL_MPIC_20_MAX_IRQ MAX_IRQ
dbda808a
FB
95
96/* Interrupt definitions */
cdbb912a 97/* IRQs, accessible through the IRQ region */
d0b72631
AG
98#define FSL_MPIC_20_EXT_IRQ 0x00
99#define FSL_MPIC_20_INT_IRQ 0x10
100#define FSL_MPIC_20_MSG_IRQ 0xb0
101#define FSL_MPIC_20_MSI_IRQ 0xe0
cdbb912a
AG
102/* These are available through separate regions, but
103 for simplicity's sake mapped into the same number space */
d0b72631
AG
104#define FSL_MPIC_20_TMR_IRQ 0x100
105#define FSL_MPIC_20_IPI_IRQ 0x104
b7169916 106
3e772232
BB
107/*
108 * Block Revision Register1 (BRR1): QEMU does not fully emulate
109 * any version on MPIC. So to start with, set the IP version to 0.
110 *
111 * NOTE: This is Freescale MPIC specific register. Keep it here till
112 * this code is refactored for different variants of OPENPIC and MPIC.
113 */
114#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */
115#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */
116#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */
117
825463b3
AG
118#define FREP_NIRQ_SHIFT 16
119#define FREP_NCPU_SHIFT 8
120#define FREP_VID_SHIFT 0
121
122#define VID_REVISION_1_2 2
d0b72631 123#define VID_REVISION_1_3 3
825463b3
AG
124
125#define VENI_GENERIC 0x00000000 /* Generic Vendor ID */
126
1945dbc1
AG
127#define IDR_EP_SHIFT 31
128#define IDR_EP_MASK (1 << IDR_EP_SHIFT)
129#define IDR_CI0_SHIFT 30
130#define IDR_CI1_SHIFT 29
131#define IDR_P1_SHIFT 1
132#define IDR_P0_SHIFT 0
b7169916 133
732aa6ec
AG
134#define MSIIR_OFFSET 0x140
135#define MSIIR_SRS_SHIFT 29
136#define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT)
137#define MSIIR_IBS_SHIFT 24
138#define MSIIR_IBS_MASK (0x1f << MSIIR_IBS_SHIFT)
139
dbda808a
FB
140#define BF_WIDTH(_bits_) \
141(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
142
143static inline void set_bit (uint32_t *field, int bit)
144{
145 field[bit >> 5] |= 1 << (bit & 0x1F);
146}
147
148static inline void reset_bit (uint32_t *field, int bit)
149{
150 field[bit >> 5] &= ~(1 << (bit & 0x1F));
151}
152
153static inline int test_bit (uint32_t *field, int bit)
154{
155 return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
156}
157
704c7e5d
AG
158static int get_current_cpu(void)
159{
160 return cpu_single_env->cpu_index;
161}
162
a8170e5e 163static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
704c7e5d 164 int idx);
a8170e5e 165static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
704c7e5d
AG
166 uint32_t val, int idx);
167
c227f099 168typedef struct IRQ_queue_t {
dbda808a
FB
169 uint32_t queue[BF_WIDTH(MAX_IRQ)];
170 int next;
171 int priority;
c227f099 172} IRQ_queue_t;
dbda808a 173
c227f099 174typedef struct IRQ_src_t {
dbda808a
FB
175 uint32_t ipvp; /* IRQ vector/priority register */
176 uint32_t ide; /* IRQ destination register */
dbda808a 177 int last_cpu;
611493d9 178 int pending; /* TRUE if IRQ is pending */
c227f099 179} IRQ_src_t;
dbda808a 180
1945dbc1
AG
181#define IPVP_MASK_SHIFT 31
182#define IPVP_MASK_MASK (1 << IPVP_MASK_SHIFT)
183#define IPVP_ACTIVITY_SHIFT 30
184#define IPVP_ACTIVITY_MASK (1 << IPVP_ACTIVITY_SHIFT)
185#define IPVP_MODE_SHIFT 29
186#define IPVP_MODE_MASK (1 << IPVP_MODE_SHIFT)
187#define IPVP_POLARITY_SHIFT 23
188#define IPVP_POLARITY_MASK (1 << IPVP_POLARITY_SHIFT)
189#define IPVP_SENSE_SHIFT 22
190#define IPVP_SENSE_MASK (1 << IPVP_SENSE_SHIFT)
191
dbda808a 192#define IPVP_PRIORITY_MASK (0x1F << 16)
611493d9 193#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16))
dbda808a
FB
194#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1)
195#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
196
c227f099 197typedef struct IRQ_dst_t {
dbda808a
FB
198 uint32_t pctp; /* CPU current task priority */
199 uint32_t pcsr; /* CPU sensitivity register */
c227f099
AL
200 IRQ_queue_t raised;
201 IRQ_queue_t servicing;
e9df014c 202 qemu_irq *irqs;
c227f099 203} IRQ_dst_t;
dbda808a 204
6d544ee8 205typedef struct OpenPICState {
d0b72631 206 SysBusDevice busdev;
23c5e4ca 207 MemoryRegion mem;
71cf9e62 208
5861a338 209 /* Behavior control */
d0b72631 210 uint32_t model;
5861a338 211 uint32_t flags;
825463b3
AG
212 uint32_t nb_irqs;
213 uint32_t vid;
214 uint32_t veni; /* Vendor identification register */
215 uint32_t spve_mask;
216 uint32_t tifr_reset;
217 uint32_t ipvp_reset;
218 uint32_t ide_reset;
dbbbfd60 219 uint32_t brr1;
5861a338 220
71cf9e62 221 /* Sub-regions */
732aa6ec 222 MemoryRegion sub_io_mem[5];
71cf9e62 223
dbda808a
FB
224 /* Global registers */
225 uint32_t frep; /* Feature reporting register */
226 uint32_t glbc; /* Global configuration register */
e9df014c 227 uint32_t pint; /* Processor initialization register */
dbda808a
FB
228 uint32_t spve; /* Spurious vector register */
229 uint32_t tifr; /* Timer frequency reporting register */
230 /* Source registers */
c227f099 231 IRQ_src_t src[MAX_IRQ];
dbda808a 232 /* Local registers per output pin */
c227f099 233 IRQ_dst_t dst[MAX_CPU];
d0b72631 234 uint32_t nb_cpus;
dbda808a
FB
235 /* Timer registers */
236 struct {
060fbfe1
AJ
237 uint32_t ticc; /* Global timer current count register */
238 uint32_t tibc; /* Global timer base count register */
dbda808a 239 } timers[MAX_TMR];
732aa6ec
AG
240 /* Shared MSI registers */
241 struct {
242 uint32_t msir; /* Shared Message Signaled Interrupt Register */
243 } msi[MAX_MSI];
d0b72631
AG
244 uint32_t max_irq;
245 uint32_t irq_ipi0;
246 uint32_t irq_tim0;
732aa6ec 247 uint32_t irq_msi;
6d544ee8 248} OpenPICState;
dbda808a 249
6d544ee8 250static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQ_src_t *src);
5861a338 251
c227f099 252static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
dbda808a
FB
253{
254 set_bit(q->queue, n_IRQ);
255}
256
c227f099 257static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
dbda808a
FB
258{
259 reset_bit(q->queue, n_IRQ);
260}
261
c227f099 262static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
dbda808a
FB
263{
264 return test_bit(q->queue, n_IRQ);
265}
266
6d544ee8 267static void IRQ_check(OpenPICState *opp, IRQ_queue_t *q)
dbda808a
FB
268{
269 int next, i;
270 int priority;
271
272 next = -1;
273 priority = -1;
b7169916 274 for (i = 0; i < opp->max_irq; i++) {
060fbfe1 275 if (IRQ_testbit(q, i)) {
5fafdf24 276 DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n",
611493d9 277 i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
060fbfe1
AJ
278 if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
279 next = i;
280 priority = IPVP_PRIORITY(opp->src[i].ipvp);
281 }
282 }
dbda808a
FB
283 }
284 q->next = next;
285 q->priority = priority;
286}
287
6d544ee8 288static int IRQ_get_next(OpenPICState *opp, IRQ_queue_t *q)
dbda808a
FB
289{
290 if (q->next == -1) {
611493d9 291 /* XXX: optimize */
060fbfe1 292 IRQ_check(opp, q);
dbda808a
FB
293 }
294
295 return q->next;
296}
297
6d544ee8 298static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ)
dbda808a 299{
c227f099
AL
300 IRQ_dst_t *dst;
301 IRQ_src_t *src;
dbda808a
FB
302 int priority;
303
304 dst = &opp->dst[n_CPU];
305 src = &opp->src[n_IRQ];
306 priority = IPVP_PRIORITY(src->ipvp);
307 if (priority <= dst->pctp) {
060fbfe1 308 /* Too low priority */
e9df014c
JM
309 DPRINTF("%s: IRQ %d has too low priority on CPU %d\n",
310 __func__, n_IRQ, n_CPU);
060fbfe1 311 return;
dbda808a
FB
312 }
313 if (IRQ_testbit(&dst->raised, n_IRQ)) {
060fbfe1 314 /* Interrupt miss */
e9df014c
JM
315 DPRINTF("%s: IRQ %d was missed on CPU %d\n",
316 __func__, n_IRQ, n_CPU);
060fbfe1 317 return;
dbda808a 318 }
1945dbc1 319 src->ipvp |= IPVP_ACTIVITY_MASK;
dbda808a 320 IRQ_setbit(&dst->raised, n_IRQ);
e9df014c
JM
321 if (priority < dst->raised.priority) {
322 /* An higher priority IRQ is already raised */
323 DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n",
324 __func__, n_IRQ, dst->raised.next, n_CPU);
325 return;
326 }
327 IRQ_get_next(opp, &dst->raised);
328 if (IRQ_get_next(opp, &dst->servicing) != -1 &&
24865167 329 priority <= dst->servicing.priority) {
e9df014c
JM
330 DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n",
331 __func__, n_IRQ, dst->servicing.next, n_CPU);
332 /* Already servicing a higher priority IRQ */
333 return;
dbda808a 334 }
e9df014c 335 DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
5861a338 336 openpic_irq_raise(opp, n_CPU, src);
dbda808a
FB
337}
338
611493d9 339/* update pic state because registers for n_IRQ have changed value */
6d544ee8 340static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
dbda808a 341{
c227f099 342 IRQ_src_t *src;
dbda808a
FB
343 int i;
344
345 src = &opp->src[n_IRQ];
611493d9
FB
346
347 if (!src->pending) {
348 /* no irq pending */
e9df014c 349 DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ);
611493d9
FB
350 return;
351 }
1945dbc1 352 if (src->ipvp & IPVP_MASK_MASK) {
060fbfe1 353 /* Interrupt source is disabled */
e9df014c 354 DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ);
060fbfe1 355 return;
dbda808a
FB
356 }
357 if (IPVP_PRIORITY(src->ipvp) == 0) {
060fbfe1 358 /* Priority set to zero */
e9df014c 359 DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ);
060fbfe1 360 return;
dbda808a 361 }
1945dbc1 362 if (src->ipvp & IPVP_ACTIVITY_MASK) {
611493d9 363 /* IRQ already active */
e9df014c 364 DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ);
611493d9
FB
365 return;
366 }
dbda808a 367 if (src->ide == 0x00000000) {
060fbfe1 368 /* No target */
e9df014c 369 DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ);
060fbfe1 370 return;
dbda808a 371 }
611493d9 372
e9df014c
JM
373 if (src->ide == (1 << src->last_cpu)) {
374 /* Only one CPU is allowed to receive this IRQ */
375 IRQ_local_pipe(opp, src->last_cpu, n_IRQ);
1945dbc1 376 } else if (!(src->ipvp & IPVP_MODE_MASK)) {
611493d9
FB
377 /* Directed delivery mode */
378 for (i = 0; i < opp->nb_cpus; i++) {
1945dbc1 379 if (src->ide & (1 << i)) {
611493d9 380 IRQ_local_pipe(opp, i, n_IRQ);
1945dbc1 381 }
611493d9 382 }
dbda808a 383 } else {
611493d9 384 /* Distributed delivery mode */
e9df014c
JM
385 for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
386 if (i == opp->nb_cpus)
611493d9 387 i = 0;
1945dbc1 388 if (src->ide & (1 << i)) {
611493d9
FB
389 IRQ_local_pipe(opp, i, n_IRQ);
390 src->last_cpu = i;
391 break;
392 }
393 }
394 }
395}
396
d537cf6c 397static void openpic_set_irq(void *opaque, int n_IRQ, int level)
611493d9 398{
6d544ee8 399 OpenPICState *opp = opaque;
c227f099 400 IRQ_src_t *src;
611493d9
FB
401
402 src = &opp->src[n_IRQ];
5fafdf24 403 DPRINTF("openpic: set irq %d = %d ipvp=%08x\n",
611493d9 404 n_IRQ, level, src->ipvp);
1945dbc1 405 if (src->ipvp & IPVP_SENSE_MASK) {
611493d9
FB
406 /* level-sensitive irq */
407 src->pending = level;
1945dbc1
AG
408 if (!level) {
409 src->ipvp &= ~IPVP_ACTIVITY_MASK;
410 }
611493d9
FB
411 } else {
412 /* edge-sensitive irq */
413 if (level)
414 src->pending = 1;
dbda808a 415 }
611493d9 416 openpic_update_irq(opp, n_IRQ);
dbda808a
FB
417}
418
d0b72631 419static void openpic_reset(DeviceState *d)
dbda808a 420{
d0b72631 421 OpenPICState *opp = FROM_SYSBUS(typeof (*opp), sysbus_from_qdev(d));
dbda808a
FB
422 int i;
423
424 opp->glbc = 0x80000000;
f8407028 425 /* Initialise controller registers */
825463b3
AG
426 opp->frep = ((opp->nb_irqs -1) << FREP_NIRQ_SHIFT) |
427 ((opp->nb_cpus -1) << FREP_NCPU_SHIFT) |
428 (opp->vid << FREP_VID_SHIFT);
429
e9df014c 430 opp->pint = 0x00000000;
825463b3
AG
431 opp->spve = -1 & opp->spve_mask;
432 opp->tifr = opp->tifr_reset;
dbda808a 433 /* Initialise IRQ sources */
b7169916 434 for (i = 0; i < opp->max_irq; i++) {
825463b3
AG
435 opp->src[i].ipvp = opp->ipvp_reset;
436 opp->src[i].ide = opp->ide_reset;
dbda808a
FB
437 }
438 /* Initialise IRQ destinations */
e9df014c 439 for (i = 0; i < MAX_CPU; i++) {
060fbfe1
AJ
440 opp->dst[i].pctp = 0x0000000F;
441 opp->dst[i].pcsr = 0x00000000;
442 memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t));
d14ed254 443 opp->dst[i].raised.next = -1;
060fbfe1 444 memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
d14ed254 445 opp->dst[i].servicing.next = -1;
dbda808a
FB
446 }
447 /* Initialise timers */
448 for (i = 0; i < MAX_TMR; i++) {
060fbfe1
AJ
449 opp->timers[i].ticc = 0x00000000;
450 opp->timers[i].tibc = 0x80000000;
dbda808a 451 }
dbda808a
FB
452 /* Go out of RESET state */
453 opp->glbc = 0x00000000;
454}
455
6d544ee8 456static inline uint32_t read_IRQreg_ide(OpenPICState *opp, int n_IRQ)
dbda808a 457{
8d3a8c1e
AG
458 return opp->src[n_IRQ].ide;
459}
dbda808a 460
6d544ee8 461static inline uint32_t read_IRQreg_ipvp(OpenPICState *opp, int n_IRQ)
8d3a8c1e
AG
462{
463 return opp->src[n_IRQ].ipvp;
dbda808a
FB
464}
465
6d544ee8 466static inline void write_IRQreg_ide(OpenPICState *opp, int n_IRQ, uint32_t val)
dbda808a
FB
467{
468 uint32_t tmp;
469
11de8b71
AG
470 tmp = val & 0xC0000000;
471 tmp |= val & ((1ULL << MAX_CPU) - 1);
472 opp->src[n_IRQ].ide = tmp;
473 DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
474}
475
6d544ee8 476static inline void write_IRQreg_ipvp(OpenPICState *opp, int n_IRQ, uint32_t val)
11de8b71
AG
477{
478 /* NOTE: not fully accurate for special IRQs, but simple and sufficient */
479 /* ACTIVITY bit is read-only */
480 opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000)
481 | (val & 0x800F00FF);
482 openpic_update_irq(opp, n_IRQ);
483 DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val,
484 opp->src[n_IRQ].ipvp);
dbda808a
FB
485}
486
b9b2aaa3
AG
487static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val,
488 unsigned len)
dbda808a 489{
6d544ee8 490 OpenPICState *opp = opaque;
c227f099 491 IRQ_dst_t *dst;
e9df014c 492 int idx;
dbda808a 493
0bf9e31a 494 DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
dbda808a
FB
495 if (addr & 0xF)
496 return;
dbda808a 497 switch (addr) {
3e772232
BB
498 case 0x00: /* Block Revision Register1 (BRR1) is Readonly */
499 break;
704c7e5d
AG
500 case 0x40:
501 case 0x50:
502 case 0x60:
503 case 0x70:
504 case 0x80:
505 case 0x90:
506 case 0xA0:
507 case 0xB0:
508 openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
dbda808a 509 break;
704c7e5d 510 case 0x1000: /* FREP */
dbda808a 511 break;
704c7e5d 512 case 0x1020: /* GLBC */
825463b3 513 if (val & 0x80000000) {
d0b72631 514 openpic_reset(&opp->busdev.qdev);
825463b3 515 }
060fbfe1 516 break;
704c7e5d 517 case 0x1080: /* VENI */
060fbfe1 518 break;
704c7e5d 519 case 0x1090: /* PINT */
e9df014c
JM
520 for (idx = 0; idx < opp->nb_cpus; idx++) {
521 if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) {
522 DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx);
523 dst = &opp->dst[idx];
524 qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]);
525 } else if (!(val & (1 << idx)) && (opp->pint & (1 << idx))) {
526 DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx);
527 dst = &opp->dst[idx];
528 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]);
529 }
dbda808a 530 }
e9df014c 531 opp->pint = val;
060fbfe1 532 break;
704c7e5d
AG
533 case 0x10A0: /* IPI_IPVP */
534 case 0x10B0:
535 case 0x10C0:
536 case 0x10D0:
dbda808a
FB
537 {
538 int idx;
704c7e5d 539 idx = (addr - 0x10A0) >> 4;
11de8b71 540 write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val);
dbda808a
FB
541 }
542 break;
704c7e5d 543 case 0x10E0: /* SPVE */
825463b3 544 opp->spve = val & opp->spve_mask;
dbda808a 545 break;
dbda808a
FB
546 default:
547 break;
548 }
549}
550
b9b2aaa3 551static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len)
dbda808a 552{
6d544ee8 553 OpenPICState *opp = opaque;
dbda808a
FB
554 uint32_t retval;
555
0bf9e31a 556 DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
dbda808a
FB
557 retval = 0xFFFFFFFF;
558 if (addr & 0xF)
559 return retval;
dbda808a 560 switch (addr) {
704c7e5d 561 case 0x1000: /* FREP */
dbda808a
FB
562 retval = opp->frep;
563 break;
704c7e5d 564 case 0x1020: /* GLBC */
dbda808a 565 retval = opp->glbc;
060fbfe1 566 break;
704c7e5d 567 case 0x1080: /* VENI */
dbda808a 568 retval = opp->veni;
060fbfe1 569 break;
704c7e5d 570 case 0x1090: /* PINT */
dbda808a 571 retval = 0x00000000;
060fbfe1 572 break;
3e772232 573 case 0x00: /* Block Revision Register1 (BRR1) */
704c7e5d
AG
574 case 0x40:
575 case 0x50:
576 case 0x60:
577 case 0x70:
578 case 0x80:
579 case 0x90:
580 case 0xA0:
dbda808a 581 case 0xB0:
704c7e5d
AG
582 retval = openpic_cpu_read_internal(opp, addr, get_current_cpu());
583 break;
584 case 0x10A0: /* IPI_IPVP */
585 case 0x10B0:
586 case 0x10C0:
587 case 0x10D0:
dbda808a
FB
588 {
589 int idx;
704c7e5d 590 idx = (addr - 0x10A0) >> 4;
8d3a8c1e 591 retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx);
dbda808a 592 }
060fbfe1 593 break;
704c7e5d 594 case 0x10E0: /* SPVE */
dbda808a
FB
595 retval = opp->spve;
596 break;
dbda808a
FB
597 default:
598 break;
599 }
600 DPRINTF("%s: => %08x\n", __func__, retval);
dbda808a
FB
601
602 return retval;
603}
604
6d544ee8 605static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val,
b9b2aaa3 606 unsigned len)
dbda808a 607{
6d544ee8 608 OpenPICState *opp = opaque;
dbda808a
FB
609 int idx;
610
611 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
612 if (addr & 0xF)
613 return;
c38c0b8a 614 idx = (addr >> 6) & 0x3;
dbda808a 615 addr = addr & 0x30;
c38c0b8a
AG
616
617 if (addr == 0x0) {
618 /* TIFR (TFRR) */
619 opp->tifr = val;
620 return;
621 }
622 switch (addr & 0x30) {
623 case 0x00: /* TICC (GTCCR) */
dbda808a 624 break;
c38c0b8a 625 case 0x10: /* TIBC (GTBCR) */
060fbfe1
AJ
626 if ((opp->timers[idx].ticc & 0x80000000) != 0 &&
627 (val & 0x80000000) == 0 &&
dbda808a 628 (opp->timers[idx].tibc & 0x80000000) != 0)
060fbfe1
AJ
629 opp->timers[idx].ticc &= ~0x80000000;
630 opp->timers[idx].tibc = val;
631 break;
c38c0b8a 632 case 0x20: /* TIVP (GTIVPR) */
11de8b71 633 write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val);
060fbfe1 634 break;
c38c0b8a 635 case 0x30: /* TIDE (GTIDR) */
11de8b71 636 write_IRQreg_ide(opp, opp->irq_tim0 + idx, val);
060fbfe1 637 break;
dbda808a
FB
638 }
639}
640
6d544ee8 641static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len)
dbda808a 642{
6d544ee8 643 OpenPICState *opp = opaque;
c38c0b8a 644 uint32_t retval = -1;
dbda808a
FB
645 int idx;
646
647 DPRINTF("%s: addr %08x\n", __func__, addr);
c38c0b8a
AG
648 if (addr & 0xF) {
649 goto out;
650 }
651 idx = (addr >> 6) & 0x3;
652 if (addr == 0x0) {
653 /* TIFR (TFRR) */
654 retval = opp->tifr;
655 goto out;
656 }
657 switch (addr & 0x30) {
658 case 0x00: /* TICC (GTCCR) */
060fbfe1 659 retval = opp->timers[idx].ticc;
dbda808a 660 break;
c38c0b8a 661 case 0x10: /* TIBC (GTBCR) */
060fbfe1
AJ
662 retval = opp->timers[idx].tibc;
663 break;
c38c0b8a 664 case 0x20: /* TIPV (TIPV) */
8d3a8c1e 665 retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx);
060fbfe1 666 break;
c38c0b8a 667 case 0x30: /* TIDE (TIDR) */
8d3a8c1e 668 retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx);
060fbfe1 669 break;
dbda808a 670 }
c38c0b8a
AG
671
672out:
dbda808a 673 DPRINTF("%s: => %08x\n", __func__, retval);
dbda808a
FB
674
675 return retval;
676}
677
b9b2aaa3
AG
678static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val,
679 unsigned len)
dbda808a 680{
6d544ee8 681 OpenPICState *opp = opaque;
dbda808a
FB
682 int idx;
683
684 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
685 if (addr & 0xF)
686 return;
dbda808a
FB
687 addr = addr & 0xFFF0;
688 idx = addr >> 5;
689 if (addr & 0x10) {
690 /* EXDE / IFEDE / IEEDE */
11de8b71 691 write_IRQreg_ide(opp, idx, val);
dbda808a
FB
692 } else {
693 /* EXVP / IFEVP / IEEVP */
11de8b71 694 write_IRQreg_ipvp(opp, idx, val);
dbda808a
FB
695 }
696}
697
b9b2aaa3 698static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len)
dbda808a 699{
6d544ee8 700 OpenPICState *opp = opaque;
dbda808a
FB
701 uint32_t retval;
702 int idx;
703
704 DPRINTF("%s: addr %08x\n", __func__, addr);
705 retval = 0xFFFFFFFF;
706 if (addr & 0xF)
707 return retval;
708 addr = addr & 0xFFF0;
709 idx = addr >> 5;
710 if (addr & 0x10) {
711 /* EXDE / IFEDE / IEEDE */
8d3a8c1e 712 retval = read_IRQreg_ide(opp, idx);
dbda808a
FB
713 } else {
714 /* EXVP / IFEVP / IEEVP */
8d3a8c1e 715 retval = read_IRQreg_ipvp(opp, idx);
dbda808a
FB
716 }
717 DPRINTF("%s: => %08x\n", __func__, retval);
dbda808a
FB
718
719 return retval;
720}
721
732aa6ec
AG
722static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val,
723 unsigned size)
724{
725 OpenPICState *opp = opaque;
726 int idx = opp->irq_msi;
727 int srs, ibs;
728
729 DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
730 if (addr & 0xF) {
731 return;
732 }
733
734 switch (addr) {
735 case MSIIR_OFFSET:
736 srs = val >> MSIIR_SRS_SHIFT;
737 idx += srs;
738 ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT;
739 opp->msi[srs].msir |= 1 << ibs;
740 openpic_set_irq(opp, idx, 1);
741 break;
742 default:
743 /* most registers are read-only, thus ignored */
744 break;
745 }
746}
747
748static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size)
749{
750 OpenPICState *opp = opaque;
751 uint64_t r = 0;
752 int i, srs;
753
754 DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
755 if (addr & 0xF) {
756 return -1;
757 }
758
759 srs = addr >> 4;
760
761 switch (addr) {
762 case 0x00:
763 case 0x10:
764 case 0x20:
765 case 0x30:
766 case 0x40:
767 case 0x50:
768 case 0x60:
769 case 0x70: /* MSIRs */
770 r = opp->msi[srs].msir;
771 /* Clear on read */
772 opp->msi[srs].msir = 0;
773 break;
774 case 0x120: /* MSISR */
775 for (i = 0; i < MAX_MSI; i++) {
776 r |= (opp->msi[i].msir ? 1 : 0) << i;
777 }
778 break;
779 }
780
781 return r;
782}
783
a8170e5e 784static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
704c7e5d 785 uint32_t val, int idx)
dbda808a 786{
6d544ee8 787 OpenPICState *opp = opaque;
c227f099
AL
788 IRQ_src_t *src;
789 IRQ_dst_t *dst;
704c7e5d 790 int s_IRQ, n_IRQ;
dbda808a 791
704c7e5d
AG
792 DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx,
793 addr, val);
dbda808a
FB
794 if (addr & 0xF)
795 return;
dbda808a
FB
796 dst = &opp->dst[idx];
797 addr &= 0xFF0;
798 switch (addr) {
704c7e5d 799 case 0x40: /* IPIDR */
dbda808a
FB
800 case 0x50:
801 case 0x60:
802 case 0x70:
803 idx = (addr - 0x40) >> 4;
a675155e 804 /* we use IDE as mask which CPUs to deliver the IPI to still. */
11de8b71
AG
805 write_IRQreg_ide(opp, opp->irq_ipi0 + idx,
806 opp->src[opp->irq_ipi0 + idx].ide | val);
b7169916
AJ
807 openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
808 openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
dbda808a 809 break;
dbda808a 810 case 0x80: /* PCTP */
060fbfe1
AJ
811 dst->pctp = val & 0x0000000F;
812 break;
dbda808a 813 case 0x90: /* WHOAMI */
060fbfe1
AJ
814 /* Read-only register */
815 break;
dbda808a 816 case 0xA0: /* PIAC */
060fbfe1
AJ
817 /* Read-only register */
818 break;
dbda808a
FB
819 case 0xB0: /* PEOI */
820 DPRINTF("PEOI\n");
060fbfe1
AJ
821 s_IRQ = IRQ_get_next(opp, &dst->servicing);
822 IRQ_resetbit(&dst->servicing, s_IRQ);
823 dst->servicing.next = -1;
824 /* Set up next servicing IRQ */
825 s_IRQ = IRQ_get_next(opp, &dst->servicing);
e9df014c
JM
826 /* Check queued interrupts. */
827 n_IRQ = IRQ_get_next(opp, &dst->raised);
828 src = &opp->src[n_IRQ];
829 if (n_IRQ != -1 &&
830 (s_IRQ == -1 ||
831 IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) {
832 DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n",
833 idx, n_IRQ);
5861a338 834 openpic_irq_raise(opp, idx, src);
e9df014c 835 }
060fbfe1 836 break;
dbda808a
FB
837 default:
838 break;
839 }
840}
841
b9b2aaa3
AG
842static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val,
843 unsigned len)
704c7e5d
AG
844{
845 openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
846}
847
a8170e5e 848static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
704c7e5d 849 int idx)
dbda808a 850{
6d544ee8 851 OpenPICState *opp = opaque;
c227f099
AL
852 IRQ_src_t *src;
853 IRQ_dst_t *dst;
dbda808a 854 uint32_t retval;
704c7e5d 855 int n_IRQ;
3b46e624 856
704c7e5d 857 DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr);
dbda808a
FB
858 retval = 0xFFFFFFFF;
859 if (addr & 0xF)
860 return retval;
dbda808a
FB
861 dst = &opp->dst[idx];
862 addr &= 0xFF0;
863 switch (addr) {
3e772232 864 case 0x00: /* Block Revision Register1 (BRR1) */
dbbbfd60 865 retval = opp->brr1;
3e772232 866 break;
dbda808a 867 case 0x80: /* PCTP */
060fbfe1
AJ
868 retval = dst->pctp;
869 break;
dbda808a 870 case 0x90: /* WHOAMI */
060fbfe1
AJ
871 retval = idx;
872 break;
dbda808a 873 case 0xA0: /* PIAC */
e9df014c
JM
874 DPRINTF("Lower OpenPIC INT output\n");
875 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
060fbfe1 876 n_IRQ = IRQ_get_next(opp, &dst->raised);
dbda808a 877 DPRINTF("PIAC: irq=%d\n", n_IRQ);
060fbfe1
AJ
878 if (n_IRQ == -1) {
879 /* No more interrupt pending */
e9df014c 880 retval = IPVP_VECTOR(opp->spve);
060fbfe1
AJ
881 } else {
882 src = &opp->src[n_IRQ];
1945dbc1 883 if (!(src->ipvp & IPVP_ACTIVITY_MASK) ||
060fbfe1
AJ
884 !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) {
885 /* - Spurious level-sensitive IRQ
886 * - Priorities has been changed
887 * and the pending IRQ isn't allowed anymore
888 */
1945dbc1 889 src->ipvp &= ~IPVP_ACTIVITY_MASK;
060fbfe1
AJ
890 retval = IPVP_VECTOR(opp->spve);
891 } else {
892 /* IRQ enter servicing state */
893 IRQ_setbit(&dst->servicing, n_IRQ);
894 retval = IPVP_VECTOR(src->ipvp);
895 }
896 IRQ_resetbit(&dst->raised, n_IRQ);
897 dst->raised.next = -1;
1945dbc1 898 if (!(src->ipvp & IPVP_SENSE_MASK)) {
611493d9 899 /* edge-sensitive IRQ */
1945dbc1 900 src->ipvp &= ~IPVP_ACTIVITY_MASK;
611493d9
FB
901 src->pending = 0;
902 }
a675155e
AG
903
904 if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) {
905 src->ide &= ~(1 << idx);
1945dbc1 906 if (src->ide && !(src->ipvp & IPVP_SENSE_MASK)) {
a675155e
AG
907 /* trigger on CPUs that didn't know about it yet */
908 openpic_set_irq(opp, n_IRQ, 1);
909 openpic_set_irq(opp, n_IRQ, 0);
910 /* if all CPUs knew about it, set active bit again */
1945dbc1 911 src->ipvp |= IPVP_ACTIVITY_MASK;
a675155e
AG
912 }
913 }
060fbfe1
AJ
914 }
915 break;
dbda808a 916 case 0xB0: /* PEOI */
060fbfe1
AJ
917 retval = 0;
918 break;
dbda808a
FB
919 default:
920 break;
921 }
922 DPRINTF("%s: => %08x\n", __func__, retval);
dbda808a
FB
923
924 return retval;
925}
926
b9b2aaa3 927static uint64_t openpic_cpu_read(void *opaque, hwaddr addr, unsigned len)
704c7e5d
AG
928{
929 return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
930}
931
35732cb4 932static const MemoryRegionOps openpic_glb_ops_le = {
780d16b7
AG
933 .write = openpic_gbl_write,
934 .read = openpic_gbl_read,
935 .endianness = DEVICE_LITTLE_ENDIAN,
936 .impl = {
937 .min_access_size = 4,
938 .max_access_size = 4,
939 },
940};
dbda808a 941
35732cb4
AG
942static const MemoryRegionOps openpic_glb_ops_be = {
943 .write = openpic_gbl_write,
944 .read = openpic_gbl_read,
945 .endianness = DEVICE_BIG_ENDIAN,
946 .impl = {
947 .min_access_size = 4,
948 .max_access_size = 4,
949 },
950};
951
952static const MemoryRegionOps openpic_tmr_ops_le = {
6d544ee8
AG
953 .write = openpic_tmr_write,
954 .read = openpic_tmr_read,
780d16b7
AG
955 .endianness = DEVICE_LITTLE_ENDIAN,
956 .impl = {
957 .min_access_size = 4,
958 .max_access_size = 4,
959 },
960};
dbda808a 961
35732cb4 962static const MemoryRegionOps openpic_tmr_ops_be = {
6d544ee8
AG
963 .write = openpic_tmr_write,
964 .read = openpic_tmr_read,
35732cb4
AG
965 .endianness = DEVICE_BIG_ENDIAN,
966 .impl = {
967 .min_access_size = 4,
968 .max_access_size = 4,
969 },
970};
971
972static const MemoryRegionOps openpic_cpu_ops_le = {
780d16b7
AG
973 .write = openpic_cpu_write,
974 .read = openpic_cpu_read,
975 .endianness = DEVICE_LITTLE_ENDIAN,
976 .impl = {
977 .min_access_size = 4,
978 .max_access_size = 4,
979 },
980};
dbda808a 981
35732cb4
AG
982static const MemoryRegionOps openpic_cpu_ops_be = {
983 .write = openpic_cpu_write,
984 .read = openpic_cpu_read,
985 .endianness = DEVICE_BIG_ENDIAN,
986 .impl = {
987 .min_access_size = 4,
988 .max_access_size = 4,
989 },
990};
991
992static const MemoryRegionOps openpic_src_ops_le = {
780d16b7
AG
993 .write = openpic_src_write,
994 .read = openpic_src_read,
23c5e4ca 995 .endianness = DEVICE_LITTLE_ENDIAN,
b9b2aaa3
AG
996 .impl = {
997 .min_access_size = 4,
998 .max_access_size = 4,
999 },
23c5e4ca
AK
1000};
1001
35732cb4
AG
1002static const MemoryRegionOps openpic_src_ops_be = {
1003 .write = openpic_src_write,
1004 .read = openpic_src_read,
1005 .endianness = DEVICE_BIG_ENDIAN,
1006 .impl = {
1007 .min_access_size = 4,
1008 .max_access_size = 4,
1009 },
1010};
1011
732aa6ec
AG
1012static const MemoryRegionOps openpic_msi_ops_le = {
1013 .read = openpic_msi_read,
1014 .write = openpic_msi_write,
1015 .endianness = DEVICE_LITTLE_ENDIAN,
1016 .impl = {
1017 .min_access_size = 4,
1018 .max_access_size = 4,
1019 },
1020};
1021
1022static const MemoryRegionOps openpic_msi_ops_be = {
1023 .read = openpic_msi_read,
1024 .write = openpic_msi_write,
1025 .endianness = DEVICE_BIG_ENDIAN,
1026 .impl = {
1027 .min_access_size = 4,
1028 .max_access_size = 4,
1029 },
1030};
1031
c227f099 1032static void openpic_save_IRQ_queue(QEMUFile* f, IRQ_queue_t *q)
67b55785
BS
1033{
1034 unsigned int i;
1035
1036 for (i = 0; i < BF_WIDTH(MAX_IRQ); i++)
1037 qemu_put_be32s(f, &q->queue[i]);
1038
1039 qemu_put_sbe32s(f, &q->next);
1040 qemu_put_sbe32s(f, &q->priority);
1041}
1042
1043static void openpic_save(QEMUFile* f, void *opaque)
1044{
6d544ee8 1045 OpenPICState *opp = (OpenPICState *)opaque;
67b55785
BS
1046 unsigned int i;
1047
67b55785 1048 qemu_put_be32s(f, &opp->glbc);
67b55785
BS
1049 qemu_put_be32s(f, &opp->veni);
1050 qemu_put_be32s(f, &opp->pint);
1051 qemu_put_be32s(f, &opp->spve);
1052 qemu_put_be32s(f, &opp->tifr);
1053
b7169916 1054 for (i = 0; i < opp->max_irq; i++) {
67b55785
BS
1055 qemu_put_be32s(f, &opp->src[i].ipvp);
1056 qemu_put_be32s(f, &opp->src[i].ide);
67b55785
BS
1057 qemu_put_sbe32s(f, &opp->src[i].last_cpu);
1058 qemu_put_sbe32s(f, &opp->src[i].pending);
1059 }
1060
d0b72631 1061 qemu_put_be32s(f, &opp->nb_cpus);
b7169916
AJ
1062
1063 for (i = 0; i < opp->nb_cpus; i++) {
67b55785
BS
1064 qemu_put_be32s(f, &opp->dst[i].pctp);
1065 qemu_put_be32s(f, &opp->dst[i].pcsr);
1066 openpic_save_IRQ_queue(f, &opp->dst[i].raised);
1067 openpic_save_IRQ_queue(f, &opp->dst[i].servicing);
1068 }
1069
67b55785
BS
1070 for (i = 0; i < MAX_TMR; i++) {
1071 qemu_put_be32s(f, &opp->timers[i].ticc);
1072 qemu_put_be32s(f, &opp->timers[i].tibc);
1073 }
67b55785
BS
1074}
1075
c227f099 1076static void openpic_load_IRQ_queue(QEMUFile* f, IRQ_queue_t *q)
67b55785
BS
1077{
1078 unsigned int i;
1079
1080 for (i = 0; i < BF_WIDTH(MAX_IRQ); i++)
1081 qemu_get_be32s(f, &q->queue[i]);
1082
1083 qemu_get_sbe32s(f, &q->next);
1084 qemu_get_sbe32s(f, &q->priority);
1085}
1086
1087static int openpic_load(QEMUFile* f, void *opaque, int version_id)
1088{
6d544ee8 1089 OpenPICState *opp = (OpenPICState *)opaque;
67b55785
BS
1090 unsigned int i;
1091
1092 if (version_id != 1)
1093 return -EINVAL;
1094
67b55785 1095 qemu_get_be32s(f, &opp->glbc);
67b55785
BS
1096 qemu_get_be32s(f, &opp->veni);
1097 qemu_get_be32s(f, &opp->pint);
1098 qemu_get_be32s(f, &opp->spve);
1099 qemu_get_be32s(f, &opp->tifr);
1100
b7169916 1101 for (i = 0; i < opp->max_irq; i++) {
67b55785
BS
1102 qemu_get_be32s(f, &opp->src[i].ipvp);
1103 qemu_get_be32s(f, &opp->src[i].ide);
67b55785
BS
1104 qemu_get_sbe32s(f, &opp->src[i].last_cpu);
1105 qemu_get_sbe32s(f, &opp->src[i].pending);
1106 }
1107
d0b72631 1108 qemu_get_be32s(f, &opp->nb_cpus);
b7169916
AJ
1109
1110 for (i = 0; i < opp->nb_cpus; i++) {
67b55785
BS
1111 qemu_get_be32s(f, &opp->dst[i].pctp);
1112 qemu_get_be32s(f, &opp->dst[i].pcsr);
1113 openpic_load_IRQ_queue(f, &opp->dst[i].raised);
1114 openpic_load_IRQ_queue(f, &opp->dst[i].servicing);
1115 }
1116
67b55785
BS
1117 for (i = 0; i < MAX_TMR; i++) {
1118 qemu_get_be32s(f, &opp->timers[i].ticc);
1119 qemu_get_be32s(f, &opp->timers[i].tibc);
1120 }
1121
d0b72631 1122 return 0;
67b55785
BS
1123}
1124
6d544ee8 1125static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQ_src_t *src)
b7169916 1126{
1945dbc1 1127 int n_ci = IDR_CI0_SHIFT - n_CPU;
5861a338 1128
1945dbc1 1129 if ((opp->flags & OPENPIC_FLAG_IDE_CRIT) && (src->ide & (1 << n_ci))) {
5861a338
AG
1130 qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]);
1131 } else {
1132 qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
1133 }
b7169916
AJ
1134}
1135
d0b72631
AG
1136struct memreg {
1137 const char *name;
1138 MemoryRegionOps const *ops;
732aa6ec 1139 bool map;
d0b72631
AG
1140 hwaddr start_addr;
1141 ram_addr_t size;
1142};
1143
1144static int openpic_init(SysBusDevice *dev)
dbda808a 1145{
d0b72631
AG
1146 OpenPICState *opp = FROM_SYSBUS(typeof (*opp), dev);
1147 int i, j;
732aa6ec
AG
1148 struct memreg list_le[] = {
1149 {"glb", &openpic_glb_ops_le, true,
1150 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
1151 {"tmr", &openpic_tmr_ops_le, true,
1152 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
1153 {"msi", &openpic_msi_ops_le, true,
1154 OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
1155 {"src", &openpic_src_ops_le, true,
1156 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
1157 {"cpu", &openpic_cpu_ops_le, true,
1158 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
780d16b7 1159 };
732aa6ec
AG
1160 struct memreg list_be[] = {
1161 {"glb", &openpic_glb_ops_be, true,
1162 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
1163 {"tmr", &openpic_tmr_ops_be, true,
1164 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
1165 {"msi", &openpic_msi_ops_be, true,
1166 OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
1167 {"src", &openpic_src_ops_be, true,
1168 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
1169 {"cpu", &openpic_cpu_ops_be, true,
1170 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
d0b72631 1171 };
732aa6ec 1172 struct memreg *list;
3b46e624 1173
d0b72631
AG
1174 switch (opp->model) {
1175 case OPENPIC_MODEL_FSL_MPIC_20:
1176 default:
1177 opp->flags |= OPENPIC_FLAG_IDE_CRIT;
1178 opp->nb_irqs = 80;
1179 opp->vid = VID_REVISION_1_2;
1180 opp->veni = VENI_GENERIC;
1181 opp->spve_mask = 0xFFFF;
1182 opp->tifr_reset = 0x00000000;
1183 opp->ipvp_reset = 0x80000000;
1184 opp->ide_reset = 0x00000001;
1185 opp->max_irq = FSL_MPIC_20_MAX_IRQ;
1186 opp->irq_ipi0 = FSL_MPIC_20_IPI_IRQ;
1187 opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ;
732aa6ec 1188 opp->irq_msi = FSL_MPIC_20_MSI_IRQ;
dbbbfd60 1189 opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN;
732aa6ec 1190 msi_supported = true;
d0b72631
AG
1191 list = list_be;
1192 break;
1193 case OPENPIC_MODEL_RAVEN:
1194 opp->nb_irqs = RAVEN_MAX_EXT;
1195 opp->vid = VID_REVISION_1_3;
1196 opp->veni = VENI_GENERIC;
1197 opp->spve_mask = 0xFF;
1198 opp->tifr_reset = 0x003F7A00;
1199 opp->ipvp_reset = 0xA0000000;
1200 opp->ide_reset = 0x00000000;
1201 opp->max_irq = RAVEN_MAX_IRQ;
1202 opp->irq_ipi0 = RAVEN_IPI_IRQ;
1203 opp->irq_tim0 = RAVEN_TMR_IRQ;
dbbbfd60 1204 opp->brr1 = -1;
d0b72631 1205 list = list_le;
732aa6ec
AG
1206 /* Don't map MSI region */
1207 list[2].map = false;
d0b72631
AG
1208
1209 /* Only UP supported today */
1210 if (opp->nb_cpus != 1) {
1211 return -EINVAL;
1212 }
1213 break;
1214 }
780d16b7
AG
1215
1216 memory_region_init(&opp->mem, "openpic", 0x40000);
1217
d0b72631 1218 for (i = 0; i < ARRAY_SIZE(list_le); i++) {
732aa6ec
AG
1219 if (!list[i].map) {
1220 continue;
1221 }
1222
780d16b7
AG
1223 memory_region_init_io(&opp->sub_io_mem[i], list[i].ops, opp,
1224 list[i].name, list[i].size);
1225
1226 memory_region_add_subregion(&opp->mem, list[i].start_addr,
1227 &opp->sub_io_mem[i]);
1228 }
3b46e624 1229
d0b72631
AG
1230 for (i = 0; i < opp->nb_cpus; i++) {
1231 opp->dst[i].irqs = g_new(qemu_irq, OPENPIC_OUTPUT_NB);
1232 for (j = 0; j < OPENPIC_OUTPUT_NB; j++) {
1233 sysbus_init_irq(dev, &opp->dst[i].irqs[j]);
1234 }
1235 }
1236
1237 register_savevm(&opp->busdev.qdev, "openpic", 0, 2,
0be71e32 1238 openpic_save, openpic_load, opp);
b7169916 1239
d0b72631
AG
1240 sysbus_init_mmio(dev, &opp->mem);
1241 qdev_init_gpio_in(&dev->qdev, openpic_set_irq, opp->max_irq);
e9df014c 1242
d0b72631 1243 return 0;
b7169916
AJ
1244}
1245
d0b72631
AG
1246static Property openpic_properties[] = {
1247 DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20),
1248 DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1),
1249 DEFINE_PROP_END_OF_LIST(),
1250};
71cf9e62 1251
d0b72631
AG
1252static void openpic_class_init(ObjectClass *klass, void *data)
1253{
1254 DeviceClass *dc = DEVICE_CLASS(klass);
1255 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
b7169916 1256
d0b72631
AG
1257 k->init = openpic_init;
1258 dc->props = openpic_properties;
1259 dc->reset = openpic_reset;
1260}
71cf9e62 1261
d0b72631
AG
1262static TypeInfo openpic_info = {
1263 .name = "openpic",
1264 .parent = TYPE_SYS_BUS_DEVICE,
1265 .instance_size = sizeof(OpenPICState),
1266 .class_init = openpic_class_init,
1267};
b7169916 1268
d0b72631
AG
1269static void openpic_register_types(void)
1270{
1271 type_register_static(&openpic_info);
dbda808a 1272}
d0b72631
AG
1273
1274type_init(openpic_register_types)