]>
Commit | Line | Data |
---|---|---|
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" | |
a2cb15b0 | 38 | #include "pci/pci.h" |
b7169916 | 39 | #include "openpic.h" |
d0b72631 | 40 | #include "sysbus.h" |
6f991980 | 41 | #include "pci/msi.h" |
e69a17f6 | 42 | #include "qemu/bitops.h" |
dbda808a | 43 | |
611493d9 | 44 | //#define DEBUG_OPENPIC |
dbda808a FB |
45 | |
46 | #ifdef DEBUG_OPENPIC | |
4c4f0e48 | 47 | static const int debug_openpic = 1; |
dbda808a | 48 | #else |
4c4f0e48 | 49 | static const int debug_openpic = 0; |
dbda808a | 50 | #endif |
dbda808a | 51 | |
4c4f0e48 SW |
52 | #define DPRINTF(fmt, ...) do { \ |
53 | if (debug_openpic) { \ | |
54 | printf(fmt , ## __VA_ARGS__); \ | |
55 | } \ | |
56 | } while (0) | |
57 | ||
cdbb912a AG |
58 | #define MAX_CPU 15 |
59 | #define MAX_SRC 256 | |
dbda808a | 60 | #define MAX_TMR 4 |
dbda808a | 61 | #define MAX_IPI 4 |
732aa6ec | 62 | #define MAX_MSI 8 |
cdbb912a | 63 | #define MAX_IRQ (MAX_SRC + MAX_IPI + MAX_TMR) |
dbda808a | 64 | #define VID 0x03 /* MPIC version ID */ |
dbda808a | 65 | |
d0b72631 | 66 | /* OpenPIC capability flags */ |
be7c236f | 67 | #define OPENPIC_FLAG_IDR_CRIT (1 << 0) |
dbda808a | 68 | |
d0b72631 | 69 | /* OpenPIC address map */ |
780d16b7 AG |
70 | #define OPENPIC_GLB_REG_START 0x0 |
71 | #define OPENPIC_GLB_REG_SIZE 0x10F0 | |
72 | #define OPENPIC_TMR_REG_START 0x10F0 | |
73 | #define OPENPIC_TMR_REG_SIZE 0x220 | |
732aa6ec AG |
74 | #define OPENPIC_MSI_REG_START 0x1600 |
75 | #define OPENPIC_MSI_REG_SIZE 0x200 | |
780d16b7 AG |
76 | #define OPENPIC_SRC_REG_START 0x10000 |
77 | #define OPENPIC_SRC_REG_SIZE (MAX_SRC * 0x20) | |
78 | #define OPENPIC_CPU_REG_START 0x20000 | |
79 | #define OPENPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) | |
80 | ||
d0b72631 AG |
81 | /* Raven */ |
82 | #define RAVEN_MAX_CPU 2 | |
83 | #define RAVEN_MAX_EXT 48 | |
84 | #define RAVEN_MAX_IRQ 64 | |
85 | #define RAVEN_MAX_TMR MAX_TMR | |
86 | #define RAVEN_MAX_IPI MAX_IPI | |
87 | ||
88 | /* Interrupt definitions */ | |
89 | #define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */ | |
90 | #define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */ | |
91 | #define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */ | |
92 | #define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */ | |
93 | /* First doorbell IRQ */ | |
94 | #define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) | |
95 | ||
96 | /* FSL_MPIC_20 */ | |
97 | #define FSL_MPIC_20_MAX_CPU 1 | |
98 | #define FSL_MPIC_20_MAX_EXT 12 | |
99 | #define FSL_MPIC_20_MAX_INT 64 | |
100 | #define FSL_MPIC_20_MAX_IRQ MAX_IRQ | |
dbda808a FB |
101 | |
102 | /* Interrupt definitions */ | |
cdbb912a | 103 | /* IRQs, accessible through the IRQ region */ |
d0b72631 AG |
104 | #define FSL_MPIC_20_EXT_IRQ 0x00 |
105 | #define FSL_MPIC_20_INT_IRQ 0x10 | |
106 | #define FSL_MPIC_20_MSG_IRQ 0xb0 | |
107 | #define FSL_MPIC_20_MSI_IRQ 0xe0 | |
cdbb912a AG |
108 | /* These are available through separate regions, but |
109 | for simplicity's sake mapped into the same number space */ | |
d0b72631 AG |
110 | #define FSL_MPIC_20_TMR_IRQ 0x100 |
111 | #define FSL_MPIC_20_IPI_IRQ 0x104 | |
b7169916 | 112 | |
3e772232 BB |
113 | /* |
114 | * Block Revision Register1 (BRR1): QEMU does not fully emulate | |
115 | * any version on MPIC. So to start with, set the IP version to 0. | |
116 | * | |
117 | * NOTE: This is Freescale MPIC specific register. Keep it here till | |
118 | * this code is refactored for different variants of OPENPIC and MPIC. | |
119 | */ | |
120 | #define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */ | |
121 | #define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */ | |
122 | #define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */ | |
123 | ||
be7c236f SW |
124 | #define FRR_NIRQ_SHIFT 16 |
125 | #define FRR_NCPU_SHIFT 8 | |
126 | #define FRR_VID_SHIFT 0 | |
825463b3 AG |
127 | |
128 | #define VID_REVISION_1_2 2 | |
d0b72631 | 129 | #define VID_REVISION_1_3 3 |
825463b3 | 130 | |
be7c236f | 131 | #define VIR_GENERIC 0x00000000 /* Generic Vendor ID */ |
825463b3 | 132 | |
be7c236f | 133 | #define GCR_RESET 0x80000000 |
71c6cacb | 134 | |
be7c236f SW |
135 | #define TBCR_CI 0x80000000 /* count inhibit */ |
136 | #define TCCR_TOG 0x80000000 /* toggles when decrement to zero */ | |
71c6cacb | 137 | |
1945dbc1 AG |
138 | #define IDR_EP_SHIFT 31 |
139 | #define IDR_EP_MASK (1 << IDR_EP_SHIFT) | |
140 | #define IDR_CI0_SHIFT 30 | |
141 | #define IDR_CI1_SHIFT 29 | |
142 | #define IDR_P1_SHIFT 1 | |
143 | #define IDR_P0_SHIFT 0 | |
b7169916 | 144 | |
732aa6ec AG |
145 | #define MSIIR_OFFSET 0x140 |
146 | #define MSIIR_SRS_SHIFT 29 | |
147 | #define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT) | |
148 | #define MSIIR_IBS_SHIFT 24 | |
149 | #define MSIIR_IBS_MASK (0x1f << MSIIR_IBS_SHIFT) | |
150 | ||
704c7e5d AG |
151 | static int get_current_cpu(void) |
152 | { | |
c3203fa5 SW |
153 | if (!cpu_single_env) { |
154 | return -1; | |
155 | } | |
156 | ||
157 | return cpu_single_env->cpu_index; | |
704c7e5d AG |
158 | } |
159 | ||
a8170e5e | 160 | static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, |
704c7e5d | 161 | int idx); |
a8170e5e | 162 | static void openpic_cpu_write_internal(void *opaque, hwaddr addr, |
704c7e5d AG |
163 | uint32_t val, int idx); |
164 | ||
af7e9e74 | 165 | typedef struct IRQQueue { |
e69a17f6 SW |
166 | /* Round up to the nearest 64 IRQs so that the queue length |
167 | * won't change when moving between 32 and 64 bit hosts. | |
168 | */ | |
169 | unsigned long queue[BITS_TO_LONGS((MAX_IRQ + 63) & ~63)]; | |
dbda808a FB |
170 | int next; |
171 | int priority; | |
af7e9e74 | 172 | } IRQQueue; |
dbda808a | 173 | |
af7e9e74 | 174 | typedef struct IRQSource { |
be7c236f SW |
175 | uint32_t ivpr; /* IRQ vector/priority register */ |
176 | uint32_t idr; /* IRQ destination register */ | |
5e22c276 | 177 | uint32_t destmask; /* bitmap of CPU destinations */ |
dbda808a | 178 | int last_cpu; |
5e22c276 | 179 | int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */ |
611493d9 | 180 | int pending; /* TRUE if IRQ is pending */ |
72c1da2c | 181 | bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */ |
af7e9e74 | 182 | } IRQSource; |
dbda808a | 183 | |
be7c236f SW |
184 | #define IVPR_MASK_SHIFT 31 |
185 | #define IVPR_MASK_MASK (1 << IVPR_MASK_SHIFT) | |
186 | #define IVPR_ACTIVITY_SHIFT 30 | |
187 | #define IVPR_ACTIVITY_MASK (1 << IVPR_ACTIVITY_SHIFT) | |
188 | #define IVPR_MODE_SHIFT 29 | |
189 | #define IVPR_MODE_MASK (1 << IVPR_MODE_SHIFT) | |
190 | #define IVPR_POLARITY_SHIFT 23 | |
191 | #define IVPR_POLARITY_MASK (1 << IVPR_POLARITY_SHIFT) | |
192 | #define IVPR_SENSE_SHIFT 22 | |
193 | #define IVPR_SENSE_MASK (1 << IVPR_SENSE_SHIFT) | |
194 | ||
195 | #define IVPR_PRIORITY_MASK (0xF << 16) | |
196 | #define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16)) | |
197 | #define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask) | |
198 | ||
199 | /* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */ | |
200 | #define IDR_EP 0x80000000 /* external pin */ | |
201 | #define IDR_CI 0x40000000 /* critical interrupt */ | |
71c6cacb | 202 | |
af7e9e74 | 203 | typedef struct IRQDest { |
eb438427 | 204 | int32_t ctpr; /* CPU current task priority */ |
af7e9e74 AG |
205 | IRQQueue raised; |
206 | IRQQueue servicing; | |
e9df014c | 207 | qemu_irq *irqs; |
af7e9e74 | 208 | } IRQDest; |
dbda808a | 209 | |
6d544ee8 | 210 | typedef struct OpenPICState { |
d0b72631 | 211 | SysBusDevice busdev; |
23c5e4ca | 212 | MemoryRegion mem; |
71cf9e62 | 213 | |
5861a338 | 214 | /* Behavior control */ |
d0b72631 | 215 | uint32_t model; |
5861a338 | 216 | uint32_t flags; |
825463b3 AG |
217 | uint32_t nb_irqs; |
218 | uint32_t vid; | |
be7c236f | 219 | uint32_t vir; /* Vendor identification register */ |
0fe04622 | 220 | uint32_t vector_mask; |
be7c236f SW |
221 | uint32_t tfrr_reset; |
222 | uint32_t ivpr_reset; | |
223 | uint32_t idr_reset; | |
dbbbfd60 | 224 | uint32_t brr1; |
5861a338 | 225 | |
71cf9e62 | 226 | /* Sub-regions */ |
732aa6ec | 227 | MemoryRegion sub_io_mem[5]; |
71cf9e62 | 228 | |
dbda808a | 229 | /* Global registers */ |
be7c236f SW |
230 | uint32_t frr; /* Feature reporting register */ |
231 | uint32_t gcr; /* Global configuration register */ | |
232 | uint32_t pir; /* Processor initialization register */ | |
dbda808a | 233 | uint32_t spve; /* Spurious vector register */ |
be7c236f | 234 | uint32_t tfrr; /* Timer frequency reporting register */ |
dbda808a | 235 | /* Source registers */ |
af7e9e74 | 236 | IRQSource src[MAX_IRQ]; |
dbda808a | 237 | /* Local registers per output pin */ |
af7e9e74 | 238 | IRQDest dst[MAX_CPU]; |
d0b72631 | 239 | uint32_t nb_cpus; |
dbda808a FB |
240 | /* Timer registers */ |
241 | struct { | |
be7c236f SW |
242 | uint32_t tccr; /* Global timer current count register */ |
243 | uint32_t tbcr; /* Global timer base count register */ | |
dbda808a | 244 | } timers[MAX_TMR]; |
732aa6ec AG |
245 | /* Shared MSI registers */ |
246 | struct { | |
247 | uint32_t msir; /* Shared Message Signaled Interrupt Register */ | |
248 | } msi[MAX_MSI]; | |
d0b72631 AG |
249 | uint32_t max_irq; |
250 | uint32_t irq_ipi0; | |
251 | uint32_t irq_tim0; | |
732aa6ec | 252 | uint32_t irq_msi; |
6d544ee8 | 253 | } OpenPICState; |
dbda808a | 254 | |
af7e9e74 | 255 | static inline void IRQ_setbit(IRQQueue *q, int n_IRQ) |
dbda808a | 256 | { |
e69a17f6 | 257 | set_bit(n_IRQ, q->queue); |
dbda808a FB |
258 | } |
259 | ||
af7e9e74 | 260 | static inline void IRQ_resetbit(IRQQueue *q, int n_IRQ) |
dbda808a | 261 | { |
e69a17f6 | 262 | clear_bit(n_IRQ, q->queue); |
dbda808a FB |
263 | } |
264 | ||
af7e9e74 | 265 | static inline int IRQ_testbit(IRQQueue *q, int n_IRQ) |
dbda808a | 266 | { |
e69a17f6 | 267 | return test_bit(n_IRQ, q->queue); |
dbda808a FB |
268 | } |
269 | ||
af7e9e74 | 270 | static void IRQ_check(OpenPICState *opp, IRQQueue *q) |
dbda808a FB |
271 | { |
272 | int next, i; | |
273 | int priority; | |
274 | ||
275 | next = -1; | |
276 | priority = -1; | |
b7169916 | 277 | for (i = 0; i < opp->max_irq; i++) { |
060fbfe1 | 278 | if (IRQ_testbit(q, i)) { |
be7c236f SW |
279 | DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n", |
280 | i, IVPR_PRIORITY(opp->src[i].ivpr), priority); | |
281 | if (IVPR_PRIORITY(opp->src[i].ivpr) > priority) { | |
060fbfe1 | 282 | next = i; |
be7c236f | 283 | priority = IVPR_PRIORITY(opp->src[i].ivpr); |
060fbfe1 AJ |
284 | } |
285 | } | |
dbda808a FB |
286 | } |
287 | q->next = next; | |
288 | q->priority = priority; | |
289 | } | |
290 | ||
af7e9e74 | 291 | static int IRQ_get_next(OpenPICState *opp, IRQQueue *q) |
dbda808a | 292 | { |
3c94378e SW |
293 | /* XXX: optimize */ |
294 | IRQ_check(opp, q); | |
dbda808a FB |
295 | |
296 | return q->next; | |
297 | } | |
298 | ||
6d544ee8 | 299 | static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ) |
dbda808a | 300 | { |
af7e9e74 AG |
301 | IRQDest *dst; |
302 | IRQSource *src; | |
dbda808a FB |
303 | int priority; |
304 | ||
305 | dst = &opp->dst[n_CPU]; | |
306 | src = &opp->src[n_IRQ]; | |
5e22c276 SW |
307 | |
308 | if (src->output != OPENPIC_OUTPUT_INT) { | |
309 | /* On Freescale MPIC, critical interrupts ignore priority, | |
310 | * IACK, EOI, etc. Before MPIC v4.1 they also ignore | |
311 | * masking. | |
312 | */ | |
313 | src->ivpr |= IVPR_ACTIVITY_MASK; | |
314 | DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n", | |
315 | __func__, src->output, n_CPU, n_IRQ); | |
316 | qemu_irq_raise(opp->dst[n_CPU].irqs[src->output]); | |
317 | return; | |
318 | } | |
319 | ||
be7c236f SW |
320 | priority = IVPR_PRIORITY(src->ivpr); |
321 | if (priority <= dst->ctpr) { | |
060fbfe1 | 322 | /* Too low priority */ |
e9df014c JM |
323 | DPRINTF("%s: IRQ %d has too low priority on CPU %d\n", |
324 | __func__, n_IRQ, n_CPU); | |
060fbfe1 | 325 | return; |
dbda808a FB |
326 | } |
327 | if (IRQ_testbit(&dst->raised, n_IRQ)) { | |
060fbfe1 | 328 | /* Interrupt miss */ |
e9df014c JM |
329 | DPRINTF("%s: IRQ %d was missed on CPU %d\n", |
330 | __func__, n_IRQ, n_CPU); | |
060fbfe1 | 331 | return; |
dbda808a | 332 | } |
be7c236f | 333 | src->ivpr |= IVPR_ACTIVITY_MASK; |
dbda808a | 334 | IRQ_setbit(&dst->raised, n_IRQ); |
e9df014c JM |
335 | if (priority < dst->raised.priority) { |
336 | /* An higher priority IRQ is already raised */ | |
337 | DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n", | |
338 | __func__, n_IRQ, dst->raised.next, n_CPU); | |
339 | return; | |
340 | } | |
3c94378e | 341 | IRQ_check(opp, &dst->raised); |
e9df014c | 342 | if (IRQ_get_next(opp, &dst->servicing) != -1 && |
24865167 | 343 | priority <= dst->servicing.priority) { |
e9df014c JM |
344 | DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n", |
345 | __func__, n_IRQ, dst->servicing.next, n_CPU); | |
346 | /* Already servicing a higher priority IRQ */ | |
347 | return; | |
dbda808a | 348 | } |
e9df014c | 349 | DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ); |
5e22c276 | 350 | qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); |
dbda808a FB |
351 | } |
352 | ||
611493d9 | 353 | /* update pic state because registers for n_IRQ have changed value */ |
6d544ee8 | 354 | static void openpic_update_irq(OpenPICState *opp, int n_IRQ) |
dbda808a | 355 | { |
af7e9e74 | 356 | IRQSource *src; |
dbda808a FB |
357 | int i; |
358 | ||
359 | src = &opp->src[n_IRQ]; | |
611493d9 FB |
360 | |
361 | if (!src->pending) { | |
362 | /* no irq pending */ | |
e9df014c | 363 | DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ); |
611493d9 FB |
364 | return; |
365 | } | |
72c1da2c | 366 | if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) { |
060fbfe1 | 367 | /* Interrupt source is disabled */ |
e9df014c | 368 | DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); |
060fbfe1 | 369 | return; |
dbda808a | 370 | } |
be7c236f | 371 | if (IVPR_PRIORITY(src->ivpr) == 0) { |
060fbfe1 | 372 | /* Priority set to zero */ |
e9df014c | 373 | DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ); |
060fbfe1 | 374 | return; |
dbda808a | 375 | } |
be7c236f | 376 | if (src->ivpr & IVPR_ACTIVITY_MASK) { |
611493d9 | 377 | /* IRQ already active */ |
e9df014c | 378 | DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ); |
611493d9 FB |
379 | return; |
380 | } | |
be7c236f | 381 | if (src->idr == 0) { |
060fbfe1 | 382 | /* No target */ |
e9df014c | 383 | DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); |
060fbfe1 | 384 | return; |
dbda808a | 385 | } |
611493d9 | 386 | |
be7c236f | 387 | if (src->idr == (1 << src->last_cpu)) { |
e9df014c JM |
388 | /* Only one CPU is allowed to receive this IRQ */ |
389 | IRQ_local_pipe(opp, src->last_cpu, n_IRQ); | |
be7c236f | 390 | } else if (!(src->ivpr & IVPR_MODE_MASK)) { |
611493d9 FB |
391 | /* Directed delivery mode */ |
392 | for (i = 0; i < opp->nb_cpus; i++) { | |
5e22c276 | 393 | if (src->destmask & (1 << i)) { |
611493d9 | 394 | IRQ_local_pipe(opp, i, n_IRQ); |
1945dbc1 | 395 | } |
611493d9 | 396 | } |
dbda808a | 397 | } else { |
611493d9 | 398 | /* Distributed delivery mode */ |
e9df014c | 399 | for (i = src->last_cpu + 1; i != src->last_cpu; i++) { |
af7e9e74 | 400 | if (i == opp->nb_cpus) { |
611493d9 | 401 | i = 0; |
af7e9e74 | 402 | } |
5e22c276 | 403 | if (src->destmask & (1 << i)) { |
611493d9 FB |
404 | IRQ_local_pipe(opp, i, n_IRQ); |
405 | src->last_cpu = i; | |
406 | break; | |
407 | } | |
408 | } | |
409 | } | |
410 | } | |
411 | ||
d537cf6c | 412 | static void openpic_set_irq(void *opaque, int n_IRQ, int level) |
611493d9 | 413 | { |
6d544ee8 | 414 | OpenPICState *opp = opaque; |
af7e9e74 | 415 | IRQSource *src; |
611493d9 | 416 | |
65b9d0d5 SW |
417 | if (n_IRQ >= MAX_IRQ) { |
418 | fprintf(stderr, "%s: IRQ %d out of range\n", __func__, n_IRQ); | |
419 | abort(); | |
420 | } | |
421 | ||
611493d9 | 422 | src = &opp->src[n_IRQ]; |
be7c236f SW |
423 | DPRINTF("openpic: set irq %d = %d ivpr=0x%08x\n", |
424 | n_IRQ, level, src->ivpr); | |
425 | if (src->ivpr & IVPR_SENSE_MASK) { | |
611493d9 FB |
426 | /* level-sensitive irq */ |
427 | src->pending = level; | |
1945dbc1 | 428 | if (!level) { |
be7c236f | 429 | src->ivpr &= ~IVPR_ACTIVITY_MASK; |
1945dbc1 | 430 | } |
611493d9 FB |
431 | } else { |
432 | /* edge-sensitive irq */ | |
af7e9e74 | 433 | if (level) { |
611493d9 | 434 | src->pending = 1; |
af7e9e74 | 435 | } |
dbda808a | 436 | } |
611493d9 | 437 | openpic_update_irq(opp, n_IRQ); |
dbda808a FB |
438 | } |
439 | ||
d0b72631 | 440 | static void openpic_reset(DeviceState *d) |
dbda808a | 441 | { |
d0b72631 | 442 | OpenPICState *opp = FROM_SYSBUS(typeof (*opp), sysbus_from_qdev(d)); |
dbda808a FB |
443 | int i; |
444 | ||
be7c236f | 445 | opp->gcr = GCR_RESET; |
f8407028 | 446 | /* Initialise controller registers */ |
be7c236f SW |
447 | opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) | |
448 | ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) | | |
449 | (opp->vid << FRR_VID_SHIFT); | |
825463b3 | 450 | |
be7c236f | 451 | opp->pir = 0; |
0fe04622 | 452 | opp->spve = -1 & opp->vector_mask; |
be7c236f | 453 | opp->tfrr = opp->tfrr_reset; |
dbda808a | 454 | /* Initialise IRQ sources */ |
b7169916 | 455 | for (i = 0; i < opp->max_irq; i++) { |
be7c236f SW |
456 | opp->src[i].ivpr = opp->ivpr_reset; |
457 | opp->src[i].idr = opp->idr_reset; | |
dbda808a FB |
458 | } |
459 | /* Initialise IRQ destinations */ | |
e9df014c | 460 | for (i = 0; i < MAX_CPU; i++) { |
be7c236f | 461 | opp->dst[i].ctpr = 15; |
af7e9e74 | 462 | memset(&opp->dst[i].raised, 0, sizeof(IRQQueue)); |
d14ed254 | 463 | opp->dst[i].raised.next = -1; |
af7e9e74 | 464 | memset(&opp->dst[i].servicing, 0, sizeof(IRQQueue)); |
d14ed254 | 465 | opp->dst[i].servicing.next = -1; |
dbda808a FB |
466 | } |
467 | /* Initialise timers */ | |
468 | for (i = 0; i < MAX_TMR; i++) { | |
be7c236f SW |
469 | opp->timers[i].tccr = 0; |
470 | opp->timers[i].tbcr = TBCR_CI; | |
dbda808a | 471 | } |
dbda808a | 472 | /* Go out of RESET state */ |
be7c236f | 473 | opp->gcr = 0; |
dbda808a FB |
474 | } |
475 | ||
be7c236f | 476 | static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ) |
dbda808a | 477 | { |
be7c236f | 478 | return opp->src[n_IRQ].idr; |
8d3a8c1e | 479 | } |
dbda808a | 480 | |
be7c236f | 481 | static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ) |
8d3a8c1e | 482 | { |
be7c236f | 483 | return opp->src[n_IRQ].ivpr; |
dbda808a FB |
484 | } |
485 | ||
be7c236f | 486 | static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) |
dbda808a | 487 | { |
5e22c276 SW |
488 | IRQSource *src = &opp->src[n_IRQ]; |
489 | uint32_t normal_mask = (1UL << opp->nb_cpus) - 1; | |
490 | uint32_t crit_mask = 0; | |
491 | uint32_t mask = normal_mask; | |
492 | int crit_shift = IDR_EP_SHIFT - opp->nb_cpus; | |
493 | int i; | |
494 | ||
495 | if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { | |
496 | crit_mask = mask << crit_shift; | |
497 | mask |= crit_mask | IDR_EP; | |
498 | } | |
499 | ||
500 | src->idr = val & mask; | |
501 | DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, src->idr); | |
502 | ||
503 | if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { | |
504 | if (src->idr & crit_mask) { | |
505 | if (src->idr & normal_mask) { | |
506 | DPRINTF("%s: IRQ configured for multiple output types, using " | |
507 | "critical\n", __func__); | |
508 | } | |
509 | ||
510 | src->output = OPENPIC_OUTPUT_CINT; | |
72c1da2c | 511 | src->nomask = true; |
5e22c276 SW |
512 | src->destmask = 0; |
513 | ||
514 | for (i = 0; i < opp->nb_cpus; i++) { | |
515 | int n_ci = IDR_CI0_SHIFT - i; | |
dbda808a | 516 | |
5e22c276 SW |
517 | if (src->idr & (1UL << n_ci)) { |
518 | src->destmask |= 1UL << i; | |
519 | } | |
520 | } | |
521 | } else { | |
522 | src->output = OPENPIC_OUTPUT_INT; | |
72c1da2c | 523 | src->nomask = false; |
5e22c276 SW |
524 | src->destmask = src->idr & normal_mask; |
525 | } | |
526 | } else { | |
527 | src->destmask = src->idr; | |
528 | } | |
11de8b71 AG |
529 | } |
530 | ||
be7c236f | 531 | static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) |
11de8b71 AG |
532 | { |
533 | /* NOTE: not fully accurate for special IRQs, but simple and sufficient */ | |
534 | /* ACTIVITY bit is read-only */ | |
be7c236f SW |
535 | opp->src[n_IRQ].ivpr = (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | |
536 | (val & (IVPR_MASK_MASK | IVPR_PRIORITY_MASK | opp->vector_mask)); | |
11de8b71 | 537 | openpic_update_irq(opp, n_IRQ); |
be7c236f SW |
538 | DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val, |
539 | opp->src[n_IRQ].ivpr); | |
dbda808a FB |
540 | } |
541 | ||
b9b2aaa3 AG |
542 | static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, |
543 | unsigned len) | |
dbda808a | 544 | { |
6d544ee8 | 545 | OpenPICState *opp = opaque; |
af7e9e74 | 546 | IRQDest *dst; |
e9df014c | 547 | int idx; |
dbda808a | 548 | |
4c4f0e48 SW |
549 | DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", |
550 | __func__, addr, val); | |
af7e9e74 | 551 | if (addr & 0xF) { |
dbda808a | 552 | return; |
af7e9e74 | 553 | } |
dbda808a | 554 | switch (addr) { |
3e772232 BB |
555 | case 0x00: /* Block Revision Register1 (BRR1) is Readonly */ |
556 | break; | |
704c7e5d AG |
557 | case 0x40: |
558 | case 0x50: | |
559 | case 0x60: | |
560 | case 0x70: | |
561 | case 0x80: | |
562 | case 0x90: | |
563 | case 0xA0: | |
564 | case 0xB0: | |
565 | openpic_cpu_write_internal(opp, addr, val, get_current_cpu()); | |
dbda808a | 566 | break; |
be7c236f | 567 | case 0x1000: /* FRR */ |
dbda808a | 568 | break; |
be7c236f SW |
569 | case 0x1020: /* GCR */ |
570 | if (val & GCR_RESET) { | |
d0b72631 | 571 | openpic_reset(&opp->busdev.qdev); |
825463b3 | 572 | } |
060fbfe1 | 573 | break; |
be7c236f | 574 | case 0x1080: /* VIR */ |
060fbfe1 | 575 | break; |
be7c236f | 576 | case 0x1090: /* PIR */ |
e9df014c | 577 | for (idx = 0; idx < opp->nb_cpus; idx++) { |
be7c236f | 578 | if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) { |
e9df014c JM |
579 | DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); |
580 | dst = &opp->dst[idx]; | |
581 | qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); | |
be7c236f | 582 | } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) { |
e9df014c JM |
583 | DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); |
584 | dst = &opp->dst[idx]; | |
585 | qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); | |
586 | } | |
dbda808a | 587 | } |
be7c236f | 588 | opp->pir = val; |
060fbfe1 | 589 | break; |
be7c236f | 590 | case 0x10A0: /* IPI_IVPR */ |
704c7e5d AG |
591 | case 0x10B0: |
592 | case 0x10C0: | |
593 | case 0x10D0: | |
dbda808a FB |
594 | { |
595 | int idx; | |
704c7e5d | 596 | idx = (addr - 0x10A0) >> 4; |
be7c236f | 597 | write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val); |
dbda808a FB |
598 | } |
599 | break; | |
704c7e5d | 600 | case 0x10E0: /* SPVE */ |
0fe04622 | 601 | opp->spve = val & opp->vector_mask; |
dbda808a | 602 | break; |
dbda808a FB |
603 | default: |
604 | break; | |
605 | } | |
606 | } | |
607 | ||
b9b2aaa3 | 608 | static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) |
dbda808a | 609 | { |
6d544ee8 | 610 | OpenPICState *opp = opaque; |
dbda808a FB |
611 | uint32_t retval; |
612 | ||
4c4f0e48 | 613 | DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); |
dbda808a | 614 | retval = 0xFFFFFFFF; |
af7e9e74 | 615 | if (addr & 0xF) { |
dbda808a | 616 | return retval; |
af7e9e74 | 617 | } |
dbda808a | 618 | switch (addr) { |
be7c236f SW |
619 | case 0x1000: /* FRR */ |
620 | retval = opp->frr; | |
dbda808a | 621 | break; |
be7c236f SW |
622 | case 0x1020: /* GCR */ |
623 | retval = opp->gcr; | |
060fbfe1 | 624 | break; |
be7c236f SW |
625 | case 0x1080: /* VIR */ |
626 | retval = opp->vir; | |
060fbfe1 | 627 | break; |
be7c236f | 628 | case 0x1090: /* PIR */ |
dbda808a | 629 | retval = 0x00000000; |
060fbfe1 | 630 | break; |
3e772232 | 631 | case 0x00: /* Block Revision Register1 (BRR1) */ |
0d404683 SW |
632 | retval = opp->brr1; |
633 | break; | |
704c7e5d AG |
634 | case 0x40: |
635 | case 0x50: | |
636 | case 0x60: | |
637 | case 0x70: | |
638 | case 0x80: | |
639 | case 0x90: | |
640 | case 0xA0: | |
dbda808a | 641 | case 0xB0: |
704c7e5d AG |
642 | retval = openpic_cpu_read_internal(opp, addr, get_current_cpu()); |
643 | break; | |
be7c236f | 644 | case 0x10A0: /* IPI_IVPR */ |
704c7e5d AG |
645 | case 0x10B0: |
646 | case 0x10C0: | |
647 | case 0x10D0: | |
dbda808a FB |
648 | { |
649 | int idx; | |
704c7e5d | 650 | idx = (addr - 0x10A0) >> 4; |
be7c236f | 651 | retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx); |
dbda808a | 652 | } |
060fbfe1 | 653 | break; |
704c7e5d | 654 | case 0x10E0: /* SPVE */ |
dbda808a FB |
655 | retval = opp->spve; |
656 | break; | |
dbda808a FB |
657 | default: |
658 | break; | |
659 | } | |
4c4f0e48 | 660 | DPRINTF("%s: => 0x%08x\n", __func__, retval); |
dbda808a FB |
661 | |
662 | return retval; | |
663 | } | |
664 | ||
6d544ee8 | 665 | static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, |
b9b2aaa3 | 666 | unsigned len) |
dbda808a | 667 | { |
6d544ee8 | 668 | OpenPICState *opp = opaque; |
dbda808a FB |
669 | int idx; |
670 | ||
4c4f0e48 SW |
671 | DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", |
672 | __func__, addr, val); | |
af7e9e74 | 673 | if (addr & 0xF) { |
dbda808a | 674 | return; |
af7e9e74 | 675 | } |
c38c0b8a | 676 | idx = (addr >> 6) & 0x3; |
dbda808a | 677 | addr = addr & 0x30; |
c38c0b8a AG |
678 | |
679 | if (addr == 0x0) { | |
be7c236f SW |
680 | /* TFRR */ |
681 | opp->tfrr = val; | |
c38c0b8a AG |
682 | return; |
683 | } | |
684 | switch (addr & 0x30) { | |
be7c236f | 685 | case 0x00: /* TCCR */ |
dbda808a | 686 | break; |
be7c236f SW |
687 | case 0x10: /* TBCR */ |
688 | if ((opp->timers[idx].tccr & TCCR_TOG) != 0 && | |
689 | (val & TBCR_CI) == 0 && | |
690 | (opp->timers[idx].tbcr & TBCR_CI) != 0) { | |
691 | opp->timers[idx].tccr &= ~TCCR_TOG; | |
71c6cacb | 692 | } |
be7c236f | 693 | opp->timers[idx].tbcr = val; |
060fbfe1 | 694 | break; |
be7c236f SW |
695 | case 0x20: /* TVPR */ |
696 | write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val); | |
060fbfe1 | 697 | break; |
be7c236f SW |
698 | case 0x30: /* TDR */ |
699 | write_IRQreg_idr(opp, opp->irq_tim0 + idx, val); | |
060fbfe1 | 700 | break; |
dbda808a FB |
701 | } |
702 | } | |
703 | ||
6d544ee8 | 704 | static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len) |
dbda808a | 705 | { |
6d544ee8 | 706 | OpenPICState *opp = opaque; |
c38c0b8a | 707 | uint32_t retval = -1; |
dbda808a FB |
708 | int idx; |
709 | ||
4c4f0e48 | 710 | DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); |
c38c0b8a AG |
711 | if (addr & 0xF) { |
712 | goto out; | |
713 | } | |
714 | idx = (addr >> 6) & 0x3; | |
715 | if (addr == 0x0) { | |
be7c236f SW |
716 | /* TFRR */ |
717 | retval = opp->tfrr; | |
c38c0b8a AG |
718 | goto out; |
719 | } | |
720 | switch (addr & 0x30) { | |
be7c236f SW |
721 | case 0x00: /* TCCR */ |
722 | retval = opp->timers[idx].tccr; | |
dbda808a | 723 | break; |
be7c236f SW |
724 | case 0x10: /* TBCR */ |
725 | retval = opp->timers[idx].tbcr; | |
060fbfe1 | 726 | break; |
be7c236f SW |
727 | case 0x20: /* TIPV */ |
728 | retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx); | |
060fbfe1 | 729 | break; |
c38c0b8a | 730 | case 0x30: /* TIDE (TIDR) */ |
be7c236f | 731 | retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx); |
060fbfe1 | 732 | break; |
dbda808a | 733 | } |
c38c0b8a AG |
734 | |
735 | out: | |
4c4f0e48 | 736 | DPRINTF("%s: => 0x%08x\n", __func__, retval); |
dbda808a FB |
737 | |
738 | return retval; | |
739 | } | |
740 | ||
b9b2aaa3 AG |
741 | static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val, |
742 | unsigned len) | |
dbda808a | 743 | { |
6d544ee8 | 744 | OpenPICState *opp = opaque; |
dbda808a FB |
745 | int idx; |
746 | ||
4c4f0e48 SW |
747 | DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", |
748 | __func__, addr, val); | |
af7e9e74 | 749 | if (addr & 0xF) { |
dbda808a | 750 | return; |
af7e9e74 | 751 | } |
dbda808a FB |
752 | addr = addr & 0xFFF0; |
753 | idx = addr >> 5; | |
754 | if (addr & 0x10) { | |
755 | /* EXDE / IFEDE / IEEDE */ | |
be7c236f | 756 | write_IRQreg_idr(opp, idx, val); |
dbda808a FB |
757 | } else { |
758 | /* EXVP / IFEVP / IEEVP */ | |
be7c236f | 759 | write_IRQreg_ivpr(opp, idx, val); |
dbda808a FB |
760 | } |
761 | } | |
762 | ||
b9b2aaa3 | 763 | static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) |
dbda808a | 764 | { |
6d544ee8 | 765 | OpenPICState *opp = opaque; |
dbda808a FB |
766 | uint32_t retval; |
767 | int idx; | |
768 | ||
4c4f0e48 | 769 | DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); |
dbda808a | 770 | retval = 0xFFFFFFFF; |
af7e9e74 | 771 | if (addr & 0xF) { |
dbda808a | 772 | return retval; |
af7e9e74 | 773 | } |
dbda808a FB |
774 | addr = addr & 0xFFF0; |
775 | idx = addr >> 5; | |
776 | if (addr & 0x10) { | |
777 | /* EXDE / IFEDE / IEEDE */ | |
be7c236f | 778 | retval = read_IRQreg_idr(opp, idx); |
dbda808a FB |
779 | } else { |
780 | /* EXVP / IFEVP / IEEVP */ | |
be7c236f | 781 | retval = read_IRQreg_ivpr(opp, idx); |
dbda808a | 782 | } |
4c4f0e48 | 783 | DPRINTF("%s: => 0x%08x\n", __func__, retval); |
dbda808a FB |
784 | |
785 | return retval; | |
786 | } | |
787 | ||
732aa6ec AG |
788 | static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val, |
789 | unsigned size) | |
790 | { | |
791 | OpenPICState *opp = opaque; | |
792 | int idx = opp->irq_msi; | |
793 | int srs, ibs; | |
794 | ||
4c4f0e48 SW |
795 | DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", |
796 | __func__, addr, val); | |
732aa6ec AG |
797 | if (addr & 0xF) { |
798 | return; | |
799 | } | |
800 | ||
801 | switch (addr) { | |
802 | case MSIIR_OFFSET: | |
803 | srs = val >> MSIIR_SRS_SHIFT; | |
804 | idx += srs; | |
805 | ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT; | |
806 | opp->msi[srs].msir |= 1 << ibs; | |
807 | openpic_set_irq(opp, idx, 1); | |
808 | break; | |
809 | default: | |
810 | /* most registers are read-only, thus ignored */ | |
811 | break; | |
812 | } | |
813 | } | |
814 | ||
815 | static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) | |
816 | { | |
817 | OpenPICState *opp = opaque; | |
818 | uint64_t r = 0; | |
819 | int i, srs; | |
820 | ||
4c4f0e48 | 821 | DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); |
732aa6ec AG |
822 | if (addr & 0xF) { |
823 | return -1; | |
824 | } | |
825 | ||
826 | srs = addr >> 4; | |
827 | ||
828 | switch (addr) { | |
829 | case 0x00: | |
830 | case 0x10: | |
831 | case 0x20: | |
832 | case 0x30: | |
833 | case 0x40: | |
834 | case 0x50: | |
835 | case 0x60: | |
836 | case 0x70: /* MSIRs */ | |
837 | r = opp->msi[srs].msir; | |
838 | /* Clear on read */ | |
839 | opp->msi[srs].msir = 0; | |
e99fd8af | 840 | openpic_set_irq(opp, opp->irq_msi + srs, 0); |
732aa6ec AG |
841 | break; |
842 | case 0x120: /* MSISR */ | |
843 | for (i = 0; i < MAX_MSI; i++) { | |
844 | r |= (opp->msi[i].msir ? 1 : 0) << i; | |
845 | } | |
846 | break; | |
847 | } | |
848 | ||
849 | return r; | |
850 | } | |
851 | ||
a8170e5e | 852 | static void openpic_cpu_write_internal(void *opaque, hwaddr addr, |
704c7e5d | 853 | uint32_t val, int idx) |
dbda808a | 854 | { |
6d544ee8 | 855 | OpenPICState *opp = opaque; |
af7e9e74 AG |
856 | IRQSource *src; |
857 | IRQDest *dst; | |
704c7e5d | 858 | int s_IRQ, n_IRQ; |
dbda808a | 859 | |
4c4f0e48 | 860 | DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, |
704c7e5d | 861 | addr, val); |
c3203fa5 SW |
862 | |
863 | if (idx < 0) { | |
864 | return; | |
865 | } | |
866 | ||
af7e9e74 | 867 | if (addr & 0xF) { |
dbda808a | 868 | return; |
af7e9e74 | 869 | } |
dbda808a FB |
870 | dst = &opp->dst[idx]; |
871 | addr &= 0xFF0; | |
872 | switch (addr) { | |
704c7e5d | 873 | case 0x40: /* IPIDR */ |
dbda808a FB |
874 | case 0x50: |
875 | case 0x60: | |
876 | case 0x70: | |
877 | idx = (addr - 0x40) >> 4; | |
a675155e | 878 | /* we use IDE as mask which CPUs to deliver the IPI to still. */ |
be7c236f SW |
879 | write_IRQreg_idr(opp, opp->irq_ipi0 + idx, |
880 | opp->src[opp->irq_ipi0 + idx].idr | val); | |
b7169916 AJ |
881 | openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); |
882 | openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); | |
dbda808a | 883 | break; |
be7c236f SW |
884 | case 0x80: /* CTPR */ |
885 | dst->ctpr = val & 0x0000000F; | |
060fbfe1 | 886 | break; |
dbda808a | 887 | case 0x90: /* WHOAMI */ |
060fbfe1 AJ |
888 | /* Read-only register */ |
889 | break; | |
be7c236f | 890 | case 0xA0: /* IACK */ |
060fbfe1 AJ |
891 | /* Read-only register */ |
892 | break; | |
be7c236f SW |
893 | case 0xB0: /* EOI */ |
894 | DPRINTF("EOI\n"); | |
060fbfe1 | 895 | s_IRQ = IRQ_get_next(opp, &dst->servicing); |
65b9d0d5 SW |
896 | |
897 | if (s_IRQ < 0) { | |
898 | DPRINTF("%s: EOI with no interrupt in service\n", __func__); | |
899 | break; | |
900 | } | |
901 | ||
060fbfe1 | 902 | IRQ_resetbit(&dst->servicing, s_IRQ); |
060fbfe1 AJ |
903 | /* Set up next servicing IRQ */ |
904 | s_IRQ = IRQ_get_next(opp, &dst->servicing); | |
e9df014c JM |
905 | /* Check queued interrupts. */ |
906 | n_IRQ = IRQ_get_next(opp, &dst->raised); | |
907 | src = &opp->src[n_IRQ]; | |
908 | if (n_IRQ != -1 && | |
909 | (s_IRQ == -1 || | |
be7c236f | 910 | IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) { |
e9df014c JM |
911 | DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", |
912 | idx, n_IRQ); | |
5e22c276 | 913 | qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); |
e9df014c | 914 | } |
060fbfe1 | 915 | break; |
dbda808a FB |
916 | default: |
917 | break; | |
918 | } | |
919 | } | |
920 | ||
b9b2aaa3 AG |
921 | static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val, |
922 | unsigned len) | |
704c7e5d AG |
923 | { |
924 | openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12); | |
925 | } | |
926 | ||
a8170e5e | 927 | static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, |
704c7e5d | 928 | int idx) |
dbda808a | 929 | { |
6d544ee8 | 930 | OpenPICState *opp = opaque; |
af7e9e74 AG |
931 | IRQSource *src; |
932 | IRQDest *dst; | |
dbda808a | 933 | uint32_t retval; |
704c7e5d | 934 | int n_IRQ; |
3b46e624 | 935 | |
4c4f0e48 | 936 | DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); |
dbda808a | 937 | retval = 0xFFFFFFFF; |
c3203fa5 SW |
938 | |
939 | if (idx < 0) { | |
940 | return retval; | |
941 | } | |
942 | ||
af7e9e74 | 943 | if (addr & 0xF) { |
dbda808a | 944 | return retval; |
af7e9e74 | 945 | } |
dbda808a FB |
946 | dst = &opp->dst[idx]; |
947 | addr &= 0xFF0; | |
948 | switch (addr) { | |
be7c236f SW |
949 | case 0x80: /* CTPR */ |
950 | retval = dst->ctpr; | |
060fbfe1 | 951 | break; |
dbda808a | 952 | case 0x90: /* WHOAMI */ |
060fbfe1 AJ |
953 | retval = idx; |
954 | break; | |
be7c236f | 955 | case 0xA0: /* IACK */ |
e9df014c JM |
956 | DPRINTF("Lower OpenPIC INT output\n"); |
957 | qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); | |
060fbfe1 | 958 | n_IRQ = IRQ_get_next(opp, &dst->raised); |
be7c236f | 959 | DPRINTF("IACK: irq=%d\n", n_IRQ); |
060fbfe1 AJ |
960 | if (n_IRQ == -1) { |
961 | /* No more interrupt pending */ | |
0fe04622 | 962 | retval = opp->spve; |
060fbfe1 AJ |
963 | } else { |
964 | src = &opp->src[n_IRQ]; | |
be7c236f SW |
965 | if (!(src->ivpr & IVPR_ACTIVITY_MASK) || |
966 | !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) { | |
060fbfe1 AJ |
967 | /* - Spurious level-sensitive IRQ |
968 | * - Priorities has been changed | |
969 | * and the pending IRQ isn't allowed anymore | |
970 | */ | |
be7c236f | 971 | src->ivpr &= ~IVPR_ACTIVITY_MASK; |
0fe04622 | 972 | retval = opp->spve; |
060fbfe1 AJ |
973 | } else { |
974 | /* IRQ enter servicing state */ | |
975 | IRQ_setbit(&dst->servicing, n_IRQ); | |
be7c236f | 976 | retval = IVPR_VECTOR(opp, src->ivpr); |
060fbfe1 AJ |
977 | } |
978 | IRQ_resetbit(&dst->raised, n_IRQ); | |
be7c236f | 979 | if (!(src->ivpr & IVPR_SENSE_MASK)) { |
611493d9 | 980 | /* edge-sensitive IRQ */ |
be7c236f | 981 | src->ivpr &= ~IVPR_ACTIVITY_MASK; |
611493d9 FB |
982 | src->pending = 0; |
983 | } | |
a675155e AG |
984 | |
985 | if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) { | |
be7c236f SW |
986 | src->idr &= ~(1 << idx); |
987 | if (src->idr && !(src->ivpr & IVPR_SENSE_MASK)) { | |
a675155e AG |
988 | /* trigger on CPUs that didn't know about it yet */ |
989 | openpic_set_irq(opp, n_IRQ, 1); | |
990 | openpic_set_irq(opp, n_IRQ, 0); | |
991 | /* if all CPUs knew about it, set active bit again */ | |
be7c236f | 992 | src->ivpr |= IVPR_ACTIVITY_MASK; |
a675155e AG |
993 | } |
994 | } | |
060fbfe1 AJ |
995 | } |
996 | break; | |
be7c236f | 997 | case 0xB0: /* EOI */ |
060fbfe1 AJ |
998 | retval = 0; |
999 | break; | |
dbda808a FB |
1000 | default: |
1001 | break; | |
1002 | } | |
4c4f0e48 | 1003 | DPRINTF("%s: => 0x%08x\n", __func__, retval); |
dbda808a FB |
1004 | |
1005 | return retval; | |
1006 | } | |
1007 | ||
b9b2aaa3 | 1008 | static uint64_t openpic_cpu_read(void *opaque, hwaddr addr, unsigned len) |
704c7e5d AG |
1009 | { |
1010 | return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12); | |
1011 | } | |
1012 | ||
35732cb4 | 1013 | static const MemoryRegionOps openpic_glb_ops_le = { |
780d16b7 AG |
1014 | .write = openpic_gbl_write, |
1015 | .read = openpic_gbl_read, | |
1016 | .endianness = DEVICE_LITTLE_ENDIAN, | |
1017 | .impl = { | |
1018 | .min_access_size = 4, | |
1019 | .max_access_size = 4, | |
1020 | }, | |
1021 | }; | |
dbda808a | 1022 | |
35732cb4 AG |
1023 | static const MemoryRegionOps openpic_glb_ops_be = { |
1024 | .write = openpic_gbl_write, | |
1025 | .read = openpic_gbl_read, | |
1026 | .endianness = DEVICE_BIG_ENDIAN, | |
1027 | .impl = { | |
1028 | .min_access_size = 4, | |
1029 | .max_access_size = 4, | |
1030 | }, | |
1031 | }; | |
1032 | ||
1033 | static const MemoryRegionOps openpic_tmr_ops_le = { | |
6d544ee8 AG |
1034 | .write = openpic_tmr_write, |
1035 | .read = openpic_tmr_read, | |
780d16b7 AG |
1036 | .endianness = DEVICE_LITTLE_ENDIAN, |
1037 | .impl = { | |
1038 | .min_access_size = 4, | |
1039 | .max_access_size = 4, | |
1040 | }, | |
1041 | }; | |
dbda808a | 1042 | |
35732cb4 | 1043 | static const MemoryRegionOps openpic_tmr_ops_be = { |
6d544ee8 AG |
1044 | .write = openpic_tmr_write, |
1045 | .read = openpic_tmr_read, | |
35732cb4 AG |
1046 | .endianness = DEVICE_BIG_ENDIAN, |
1047 | .impl = { | |
1048 | .min_access_size = 4, | |
1049 | .max_access_size = 4, | |
1050 | }, | |
1051 | }; | |
1052 | ||
1053 | static const MemoryRegionOps openpic_cpu_ops_le = { | |
780d16b7 AG |
1054 | .write = openpic_cpu_write, |
1055 | .read = openpic_cpu_read, | |
1056 | .endianness = DEVICE_LITTLE_ENDIAN, | |
1057 | .impl = { | |
1058 | .min_access_size = 4, | |
1059 | .max_access_size = 4, | |
1060 | }, | |
1061 | }; | |
dbda808a | 1062 | |
35732cb4 AG |
1063 | static const MemoryRegionOps openpic_cpu_ops_be = { |
1064 | .write = openpic_cpu_write, | |
1065 | .read = openpic_cpu_read, | |
1066 | .endianness = DEVICE_BIG_ENDIAN, | |
1067 | .impl = { | |
1068 | .min_access_size = 4, | |
1069 | .max_access_size = 4, | |
1070 | }, | |
1071 | }; | |
1072 | ||
1073 | static const MemoryRegionOps openpic_src_ops_le = { | |
780d16b7 AG |
1074 | .write = openpic_src_write, |
1075 | .read = openpic_src_read, | |
23c5e4ca | 1076 | .endianness = DEVICE_LITTLE_ENDIAN, |
b9b2aaa3 AG |
1077 | .impl = { |
1078 | .min_access_size = 4, | |
1079 | .max_access_size = 4, | |
1080 | }, | |
23c5e4ca AK |
1081 | }; |
1082 | ||
35732cb4 AG |
1083 | static const MemoryRegionOps openpic_src_ops_be = { |
1084 | .write = openpic_src_write, | |
1085 | .read = openpic_src_read, | |
1086 | .endianness = DEVICE_BIG_ENDIAN, | |
1087 | .impl = { | |
1088 | .min_access_size = 4, | |
1089 | .max_access_size = 4, | |
1090 | }, | |
1091 | }; | |
1092 | ||
732aa6ec AG |
1093 | static const MemoryRegionOps openpic_msi_ops_le = { |
1094 | .read = openpic_msi_read, | |
1095 | .write = openpic_msi_write, | |
1096 | .endianness = DEVICE_LITTLE_ENDIAN, | |
1097 | .impl = { | |
1098 | .min_access_size = 4, | |
1099 | .max_access_size = 4, | |
1100 | }, | |
1101 | }; | |
1102 | ||
1103 | static const MemoryRegionOps openpic_msi_ops_be = { | |
1104 | .read = openpic_msi_read, | |
1105 | .write = openpic_msi_write, | |
1106 | .endianness = DEVICE_BIG_ENDIAN, | |
1107 | .impl = { | |
1108 | .min_access_size = 4, | |
1109 | .max_access_size = 4, | |
1110 | }, | |
1111 | }; | |
1112 | ||
af7e9e74 | 1113 | static void openpic_save_IRQ_queue(QEMUFile* f, IRQQueue *q) |
67b55785 BS |
1114 | { |
1115 | unsigned int i; | |
1116 | ||
e69a17f6 SW |
1117 | for (i = 0; i < ARRAY_SIZE(q->queue); i++) { |
1118 | /* Always put the lower half of a 64-bit long first, in case we | |
1119 | * restore on a 32-bit host. The least significant bits correspond | |
1120 | * to lower IRQ numbers in the bitmap. | |
1121 | */ | |
1122 | qemu_put_be32(f, (uint32_t)q->queue[i]); | |
1123 | #if LONG_MAX > 0x7FFFFFFF | |
1124 | qemu_put_be32(f, (uint32_t)(q->queue[i] >> 32)); | |
1125 | #endif | |
1126 | } | |
67b55785 BS |
1127 | |
1128 | qemu_put_sbe32s(f, &q->next); | |
1129 | qemu_put_sbe32s(f, &q->priority); | |
1130 | } | |
1131 | ||
1132 | static void openpic_save(QEMUFile* f, void *opaque) | |
1133 | { | |
6d544ee8 | 1134 | OpenPICState *opp = (OpenPICState *)opaque; |
67b55785 BS |
1135 | unsigned int i; |
1136 | ||
be7c236f SW |
1137 | qemu_put_be32s(f, &opp->gcr); |
1138 | qemu_put_be32s(f, &opp->vir); | |
1139 | qemu_put_be32s(f, &opp->pir); | |
67b55785 | 1140 | qemu_put_be32s(f, &opp->spve); |
be7c236f | 1141 | qemu_put_be32s(f, &opp->tfrr); |
67b55785 | 1142 | |
d0b72631 | 1143 | qemu_put_be32s(f, &opp->nb_cpus); |
b7169916 AJ |
1144 | |
1145 | for (i = 0; i < opp->nb_cpus; i++) { | |
eb438427 | 1146 | qemu_put_sbe32s(f, &opp->dst[i].ctpr); |
67b55785 BS |
1147 | openpic_save_IRQ_queue(f, &opp->dst[i].raised); |
1148 | openpic_save_IRQ_queue(f, &opp->dst[i].servicing); | |
1149 | } | |
1150 | ||
67b55785 | 1151 | for (i = 0; i < MAX_TMR; i++) { |
be7c236f SW |
1152 | qemu_put_be32s(f, &opp->timers[i].tccr); |
1153 | qemu_put_be32s(f, &opp->timers[i].tbcr); | |
67b55785 | 1154 | } |
5e22c276 SW |
1155 | |
1156 | for (i = 0; i < opp->max_irq; i++) { | |
1157 | qemu_put_be32s(f, &opp->src[i].ivpr); | |
1158 | qemu_put_be32s(f, &opp->src[i].idr); | |
1159 | qemu_put_sbe32s(f, &opp->src[i].last_cpu); | |
1160 | qemu_put_sbe32s(f, &opp->src[i].pending); | |
1161 | } | |
67b55785 BS |
1162 | } |
1163 | ||
af7e9e74 | 1164 | static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q) |
67b55785 BS |
1165 | { |
1166 | unsigned int i; | |
1167 | ||
e69a17f6 SW |
1168 | for (i = 0; i < ARRAY_SIZE(q->queue); i++) { |
1169 | unsigned long val; | |
1170 | ||
1171 | val = qemu_get_be32(f); | |
1172 | #if LONG_MAX > 0x7FFFFFFF | |
1173 | val <<= 32; | |
1174 | val |= qemu_get_be32(f); | |
1175 | #endif | |
1176 | ||
1177 | q->queue[i] = val; | |
1178 | } | |
67b55785 BS |
1179 | |
1180 | qemu_get_sbe32s(f, &q->next); | |
1181 | qemu_get_sbe32s(f, &q->priority); | |
1182 | } | |
1183 | ||
1184 | static int openpic_load(QEMUFile* f, void *opaque, int version_id) | |
1185 | { | |
6d544ee8 | 1186 | OpenPICState *opp = (OpenPICState *)opaque; |
67b55785 BS |
1187 | unsigned int i; |
1188 | ||
af7e9e74 | 1189 | if (version_id != 1) { |
67b55785 | 1190 | return -EINVAL; |
af7e9e74 | 1191 | } |
67b55785 | 1192 | |
be7c236f SW |
1193 | qemu_get_be32s(f, &opp->gcr); |
1194 | qemu_get_be32s(f, &opp->vir); | |
1195 | qemu_get_be32s(f, &opp->pir); | |
67b55785 | 1196 | qemu_get_be32s(f, &opp->spve); |
be7c236f | 1197 | qemu_get_be32s(f, &opp->tfrr); |
67b55785 | 1198 | |
d0b72631 | 1199 | qemu_get_be32s(f, &opp->nb_cpus); |
b7169916 AJ |
1200 | |
1201 | for (i = 0; i < opp->nb_cpus; i++) { | |
eb438427 | 1202 | qemu_get_sbe32s(f, &opp->dst[i].ctpr); |
67b55785 BS |
1203 | openpic_load_IRQ_queue(f, &opp->dst[i].raised); |
1204 | openpic_load_IRQ_queue(f, &opp->dst[i].servicing); | |
1205 | } | |
1206 | ||
67b55785 | 1207 | for (i = 0; i < MAX_TMR; i++) { |
be7c236f SW |
1208 | qemu_get_be32s(f, &opp->timers[i].tccr); |
1209 | qemu_get_be32s(f, &opp->timers[i].tbcr); | |
67b55785 BS |
1210 | } |
1211 | ||
5e22c276 SW |
1212 | for (i = 0; i < opp->max_irq; i++) { |
1213 | uint32_t val; | |
67b55785 | 1214 | |
5e22c276 SW |
1215 | val = qemu_get_be32(f); |
1216 | write_IRQreg_idr(opp, i, val); | |
1217 | val = qemu_get_be32(f); | |
1218 | write_IRQreg_ivpr(opp, i, val); | |
5861a338 | 1219 | |
5e22c276 SW |
1220 | qemu_get_be32s(f, &opp->src[i].ivpr); |
1221 | qemu_get_be32s(f, &opp->src[i].idr); | |
1222 | qemu_get_sbe32s(f, &opp->src[i].last_cpu); | |
1223 | qemu_get_sbe32s(f, &opp->src[i].pending); | |
5861a338 | 1224 | } |
5e22c276 SW |
1225 | |
1226 | return 0; | |
b7169916 AJ |
1227 | } |
1228 | ||
af7e9e74 | 1229 | typedef struct MemReg { |
d0b72631 AG |
1230 | const char *name; |
1231 | MemoryRegionOps const *ops; | |
732aa6ec | 1232 | bool map; |
d0b72631 AG |
1233 | hwaddr start_addr; |
1234 | ram_addr_t size; | |
af7e9e74 | 1235 | } MemReg; |
d0b72631 AG |
1236 | |
1237 | static int openpic_init(SysBusDevice *dev) | |
dbda808a | 1238 | { |
d0b72631 AG |
1239 | OpenPICState *opp = FROM_SYSBUS(typeof (*opp), dev); |
1240 | int i, j; | |
af7e9e74 | 1241 | MemReg list_le[] = { |
732aa6ec AG |
1242 | {"glb", &openpic_glb_ops_le, true, |
1243 | OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, | |
1244 | {"tmr", &openpic_tmr_ops_le, true, | |
1245 | OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, | |
1246 | {"msi", &openpic_msi_ops_le, true, | |
1247 | OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, | |
1248 | {"src", &openpic_src_ops_le, true, | |
1249 | OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, | |
1250 | {"cpu", &openpic_cpu_ops_le, true, | |
1251 | OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, | |
780d16b7 | 1252 | }; |
af7e9e74 | 1253 | MemReg list_be[] = { |
732aa6ec AG |
1254 | {"glb", &openpic_glb_ops_be, true, |
1255 | OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, | |
1256 | {"tmr", &openpic_tmr_ops_be, true, | |
1257 | OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, | |
1258 | {"msi", &openpic_msi_ops_be, true, | |
1259 | OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, | |
1260 | {"src", &openpic_src_ops_be, true, | |
1261 | OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, | |
1262 | {"cpu", &openpic_cpu_ops_be, true, | |
1263 | OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, | |
d0b72631 | 1264 | }; |
af7e9e74 | 1265 | MemReg *list; |
3b46e624 | 1266 | |
d0b72631 AG |
1267 | switch (opp->model) { |
1268 | case OPENPIC_MODEL_FSL_MPIC_20: | |
1269 | default: | |
be7c236f | 1270 | opp->flags |= OPENPIC_FLAG_IDR_CRIT; |
d0b72631 AG |
1271 | opp->nb_irqs = 80; |
1272 | opp->vid = VID_REVISION_1_2; | |
be7c236f | 1273 | opp->vir = VIR_GENERIC; |
0fe04622 | 1274 | opp->vector_mask = 0xFFFF; |
be7c236f SW |
1275 | opp->tfrr_reset = 0; |
1276 | opp->ivpr_reset = IVPR_MASK_MASK; | |
1277 | opp->idr_reset = 1 << 0; | |
d0b72631 AG |
1278 | opp->max_irq = FSL_MPIC_20_MAX_IRQ; |
1279 | opp->irq_ipi0 = FSL_MPIC_20_IPI_IRQ; | |
1280 | opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ; | |
732aa6ec | 1281 | opp->irq_msi = FSL_MPIC_20_MSI_IRQ; |
dbbbfd60 | 1282 | opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; |
732aa6ec | 1283 | msi_supported = true; |
d0b72631 AG |
1284 | list = list_be; |
1285 | break; | |
1286 | case OPENPIC_MODEL_RAVEN: | |
1287 | opp->nb_irqs = RAVEN_MAX_EXT; | |
1288 | opp->vid = VID_REVISION_1_3; | |
be7c236f | 1289 | opp->vir = VIR_GENERIC; |
0fe04622 | 1290 | opp->vector_mask = 0xFF; |
be7c236f SW |
1291 | opp->tfrr_reset = 4160000; |
1292 | opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK; | |
1293 | opp->idr_reset = 0; | |
d0b72631 AG |
1294 | opp->max_irq = RAVEN_MAX_IRQ; |
1295 | opp->irq_ipi0 = RAVEN_IPI_IRQ; | |
1296 | opp->irq_tim0 = RAVEN_TMR_IRQ; | |
dbbbfd60 | 1297 | opp->brr1 = -1; |
d0b72631 | 1298 | list = list_le; |
732aa6ec AG |
1299 | /* Don't map MSI region */ |
1300 | list[2].map = false; | |
d0b72631 AG |
1301 | |
1302 | /* Only UP supported today */ | |
1303 | if (opp->nb_cpus != 1) { | |
1304 | return -EINVAL; | |
1305 | } | |
1306 | break; | |
1307 | } | |
780d16b7 AG |
1308 | |
1309 | memory_region_init(&opp->mem, "openpic", 0x40000); | |
1310 | ||
d0b72631 | 1311 | for (i = 0; i < ARRAY_SIZE(list_le); i++) { |
732aa6ec AG |
1312 | if (!list[i].map) { |
1313 | continue; | |
1314 | } | |
1315 | ||
780d16b7 AG |
1316 | memory_region_init_io(&opp->sub_io_mem[i], list[i].ops, opp, |
1317 | list[i].name, list[i].size); | |
1318 | ||
1319 | memory_region_add_subregion(&opp->mem, list[i].start_addr, | |
1320 | &opp->sub_io_mem[i]); | |
1321 | } | |
3b46e624 | 1322 | |
d0b72631 AG |
1323 | for (i = 0; i < opp->nb_cpus; i++) { |
1324 | opp->dst[i].irqs = g_new(qemu_irq, OPENPIC_OUTPUT_NB); | |
1325 | for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { | |
1326 | sysbus_init_irq(dev, &opp->dst[i].irqs[j]); | |
1327 | } | |
1328 | } | |
1329 | ||
1330 | register_savevm(&opp->busdev.qdev, "openpic", 0, 2, | |
0be71e32 | 1331 | openpic_save, openpic_load, opp); |
b7169916 | 1332 | |
d0b72631 AG |
1333 | sysbus_init_mmio(dev, &opp->mem); |
1334 | qdev_init_gpio_in(&dev->qdev, openpic_set_irq, opp->max_irq); | |
e9df014c | 1335 | |
d0b72631 | 1336 | return 0; |
b7169916 AJ |
1337 | } |
1338 | ||
d0b72631 AG |
1339 | static Property openpic_properties[] = { |
1340 | DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), | |
1341 | DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), | |
1342 | DEFINE_PROP_END_OF_LIST(), | |
1343 | }; | |
71cf9e62 | 1344 | |
d0b72631 AG |
1345 | static void openpic_class_init(ObjectClass *klass, void *data) |
1346 | { | |
1347 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1348 | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); | |
b7169916 | 1349 | |
d0b72631 AG |
1350 | k->init = openpic_init; |
1351 | dc->props = openpic_properties; | |
1352 | dc->reset = openpic_reset; | |
1353 | } | |
71cf9e62 | 1354 | |
d0b72631 AG |
1355 | static TypeInfo openpic_info = { |
1356 | .name = "openpic", | |
1357 | .parent = TYPE_SYS_BUS_DEVICE, | |
1358 | .instance_size = sizeof(OpenPICState), | |
1359 | .class_init = openpic_class_init, | |
1360 | }; | |
b7169916 | 1361 | |
d0b72631 AG |
1362 | static void openpic_register_types(void) |
1363 | { | |
1364 | type_register_static(&openpic_info); | |
dbda808a | 1365 | } |
d0b72631 AG |
1366 | |
1367 | type_init(openpic_register_types) |