blob: 1df67aed7a918fbfdb2475330a2fba7efb4f2e90 [file] [log] [blame]
Scott Woodb823f982013-04-12 14:08:43 +00001/*
2 * OpenPIC emulation
3 *
4 * Copyright (c) 2004 Jocelyn Mayer
5 * 2011 Alexander Graf
6 *
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 */
Scott Woodb823f982013-04-12 14:08:43 +000025
26#define MAX_CPU 32
27#define MAX_SRC 256
28#define MAX_TMR 4
29#define MAX_IPI 4
30#define MAX_MSI 8
31#define MAX_IRQ (MAX_SRC + MAX_IPI + MAX_TMR)
32#define VID 0x03 /* MPIC version ID */
33
34/* OpenPIC capability flags */
35#define OPENPIC_FLAG_IDR_CRIT (1 << 0)
36#define OPENPIC_FLAG_ILR (2 << 0)
37
38/* OpenPIC address map */
39#define OPENPIC_GLB_REG_START 0x0
40#define OPENPIC_GLB_REG_SIZE 0x10F0
41#define OPENPIC_TMR_REG_START 0x10F0
42#define OPENPIC_TMR_REG_SIZE 0x220
43#define OPENPIC_MSI_REG_START 0x1600
44#define OPENPIC_MSI_REG_SIZE 0x200
Scott Woodf0f5c482013-04-12 14:08:45 +000045#define OPENPIC_SUMMARY_REG_START 0x3800
46#define OPENPIC_SUMMARY_REG_SIZE 0x800
Scott Woodb823f982013-04-12 14:08:43 +000047#define OPENPIC_SRC_REG_START 0x10000
48#define OPENPIC_SRC_REG_SIZE (MAX_SRC * 0x20)
49#define OPENPIC_CPU_REG_START 0x20000
Scott Woodf0f5c482013-04-12 14:08:45 +000050#define OPENPIC_CPU_REG_SIZE (0x100 + ((MAX_CPU - 1) * 0x1000))
Scott Woodb823f982013-04-12 14:08:43 +000051
Scott Woodf0f5c482013-04-12 14:08:45 +000052struct fsl_mpic_info {
Scott Woodb823f982013-04-12 14:08:43 +000053 int max_ext;
Scott Woodf0f5c482013-04-12 14:08:45 +000054};
Scott Woodb823f982013-04-12 14:08:43 +000055
Scott Woodf0f5c482013-04-12 14:08:45 +000056static struct fsl_mpic_info fsl_mpic_20 = {
Scott Woodb823f982013-04-12 14:08:43 +000057 .max_ext = 12,
58};
59
Scott Woodf0f5c482013-04-12 14:08:45 +000060static struct fsl_mpic_info fsl_mpic_42 = {
Scott Woodb823f982013-04-12 14:08:43 +000061 .max_ext = 12,
62};
63
64#define FRR_NIRQ_SHIFT 16
65#define FRR_NCPU_SHIFT 8
66#define FRR_VID_SHIFT 0
67
68#define VID_REVISION_1_2 2
69#define VID_REVISION_1_3 3
70
71#define VIR_GENERIC 0x00000000 /* Generic Vendor ID */
72
73#define GCR_RESET 0x80000000
74#define GCR_MODE_PASS 0x00000000
75#define GCR_MODE_MIXED 0x20000000
76#define GCR_MODE_PROXY 0x60000000
77
78#define TBCR_CI 0x80000000 /* count inhibit */
79#define TCCR_TOG 0x80000000 /* toggles when decrement to zero */
80
81#define IDR_EP_SHIFT 31
82#define IDR_EP_MASK (1 << IDR_EP_SHIFT)
83#define IDR_CI0_SHIFT 30
84#define IDR_CI1_SHIFT 29
85#define IDR_P1_SHIFT 1
86#define IDR_P0_SHIFT 0
87
88#define ILR_INTTGT_MASK 0x000000ff
89#define ILR_INTTGT_INT 0x00
90#define ILR_INTTGT_CINT 0x01 /* critical */
91#define ILR_INTTGT_MCP 0x02 /* machine check */
92
Scott Woodb823f982013-04-12 14:08:43 +000093#define MSIIR_OFFSET 0x140
94#define MSIIR_SRS_SHIFT 29
95#define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT)
96#define MSIIR_IBS_SHIFT 24
97#define MSIIR_IBS_MASK (0x1f << MSIIR_IBS_SHIFT)
98
99static int get_current_cpu(void)
100{
101 CPUState *cpu_single_cpu;
102
Scott Woodf0f5c482013-04-12 14:08:45 +0000103 if (!cpu_single_env)
Scott Woodb823f982013-04-12 14:08:43 +0000104 return -1;
Scott Woodb823f982013-04-12 14:08:43 +0000105
106 cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
107 return cpu_single_cpu->cpu_index;
108}
109
Scott Woodf0f5c482013-04-12 14:08:45 +0000110static uint32_t openpic_cpu_read_internal(void *opaque, gpa_t addr, int idx);
111static void openpic_cpu_write_internal(void *opaque, gpa_t addr,
Scott Woodb823f982013-04-12 14:08:43 +0000112 uint32_t val, int idx);
113
Scott Woodf0f5c482013-04-12 14:08:45 +0000114enum irq_type {
Scott Woodb823f982013-04-12 14:08:43 +0000115 IRQ_TYPE_NORMAL = 0,
116 IRQ_TYPE_FSLINT, /* FSL internal interrupt -- level only */
117 IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */
Scott Woodf0f5c482013-04-12 14:08:45 +0000118};
Scott Woodb823f982013-04-12 14:08:43 +0000119
Scott Woodf0f5c482013-04-12 14:08:45 +0000120struct irq_queue {
Scott Woodb823f982013-04-12 14:08:43 +0000121 /* Round up to the nearest 64 IRQs so that the queue length
122 * won't change when moving between 32 and 64 bit hosts.
123 */
124 unsigned long queue[BITS_TO_LONGS((MAX_IRQ + 63) & ~63)];
125 int next;
126 int priority;
Scott Woodf0f5c482013-04-12 14:08:45 +0000127};
Scott Woodb823f982013-04-12 14:08:43 +0000128
Scott Woodf0f5c482013-04-12 14:08:45 +0000129struct irq_source {
Scott Woodb823f982013-04-12 14:08:43 +0000130 uint32_t ivpr; /* IRQ vector/priority register */
131 uint32_t idr; /* IRQ destination register */
132 uint32_t destmask; /* bitmap of CPU destinations */
133 int last_cpu;
134 int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */
135 int pending; /* TRUE if IRQ is pending */
Scott Woodf0f5c482013-04-12 14:08:45 +0000136 enum irq_type type;
Scott Woodb823f982013-04-12 14:08:43 +0000137 bool level:1; /* level-triggered */
Scott Woodf0f5c482013-04-12 14:08:45 +0000138 bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */
139};
Scott Woodb823f982013-04-12 14:08:43 +0000140
141#define IVPR_MASK_SHIFT 31
142#define IVPR_MASK_MASK (1 << IVPR_MASK_SHIFT)
143#define IVPR_ACTIVITY_SHIFT 30
144#define IVPR_ACTIVITY_MASK (1 << IVPR_ACTIVITY_SHIFT)
145#define IVPR_MODE_SHIFT 29
146#define IVPR_MODE_MASK (1 << IVPR_MODE_SHIFT)
147#define IVPR_POLARITY_SHIFT 23
148#define IVPR_POLARITY_MASK (1 << IVPR_POLARITY_SHIFT)
149#define IVPR_SENSE_SHIFT 22
150#define IVPR_SENSE_MASK (1 << IVPR_SENSE_SHIFT)
151
152#define IVPR_PRIORITY_MASK (0xF << 16)
153#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16))
154#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask)
155
156/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */
157#define IDR_EP 0x80000000 /* external pin */
158#define IDR_CI 0x40000000 /* critical interrupt */
159
Scott Woodf0f5c482013-04-12 14:08:45 +0000160struct irq_dest {
Scott Woodb823f982013-04-12 14:08:43 +0000161 int32_t ctpr; /* CPU current task priority */
Scott Woodf0f5c482013-04-12 14:08:45 +0000162 struct irq_queue raised;
163 struct irq_queue servicing;
Scott Woodb823f982013-04-12 14:08:43 +0000164 qemu_irq *irqs;
165
166 /* Count of IRQ sources asserting on non-INT outputs */
167 uint32_t outputs_active[OPENPIC_OUTPUT_NB];
Scott Woodf0f5c482013-04-12 14:08:45 +0000168};
Scott Woodb823f982013-04-12 14:08:43 +0000169
Scott Woodf0f5c482013-04-12 14:08:45 +0000170struct openpic {
Scott Woodb823f982013-04-12 14:08:43 +0000171 /* Behavior control */
Scott Woodf0f5c482013-04-12 14:08:45 +0000172 struct fsl_mpic_info *fsl;
Scott Woodb823f982013-04-12 14:08:43 +0000173 uint32_t model;
174 uint32_t flags;
175 uint32_t nb_irqs;
176 uint32_t vid;
177 uint32_t vir; /* Vendor identification register */
178 uint32_t vector_mask;
179 uint32_t tfrr_reset;
180 uint32_t ivpr_reset;
181 uint32_t idr_reset;
182 uint32_t brr1;
183 uint32_t mpic_mode_mask;
184
Scott Woodb823f982013-04-12 14:08:43 +0000185 /* Global registers */
186 uint32_t frr; /* Feature reporting register */
187 uint32_t gcr; /* Global configuration register */
188 uint32_t pir; /* Processor initialization register */
189 uint32_t spve; /* Spurious vector register */
190 uint32_t tfrr; /* Timer frequency reporting register */
191 /* Source registers */
Scott Woodf0f5c482013-04-12 14:08:45 +0000192 struct irq_source src[MAX_IRQ];
Scott Woodb823f982013-04-12 14:08:43 +0000193 /* Local registers per output pin */
Scott Woodf0f5c482013-04-12 14:08:45 +0000194 struct irq_dest dst[MAX_CPU];
Scott Woodb823f982013-04-12 14:08:43 +0000195 uint32_t nb_cpus;
196 /* Timer registers */
197 struct {
198 uint32_t tccr; /* Global timer current count register */
199 uint32_t tbcr; /* Global timer base count register */
200 } timers[MAX_TMR];
201 /* Shared MSI registers */
202 struct {
203 uint32_t msir; /* Shared Message Signaled Interrupt Register */
204 } msi[MAX_MSI];
205 uint32_t max_irq;
206 uint32_t irq_ipi0;
207 uint32_t irq_tim0;
208 uint32_t irq_msi;
Scott Woodf0f5c482013-04-12 14:08:45 +0000209};
Scott Woodb823f982013-04-12 14:08:43 +0000210
Scott Woodf0f5c482013-04-12 14:08:45 +0000211static inline void IRQ_setbit(struct irq_queue *q, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000212{
213 set_bit(n_IRQ, q->queue);
214}
215
Scott Woodf0f5c482013-04-12 14:08:45 +0000216static inline void IRQ_resetbit(struct irq_queue *q, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000217{
218 clear_bit(n_IRQ, q->queue);
219}
220
Scott Woodf0f5c482013-04-12 14:08:45 +0000221static inline int IRQ_testbit(struct irq_queue *q, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000222{
223 return test_bit(n_IRQ, q->queue);
224}
225
Scott Woodf0f5c482013-04-12 14:08:45 +0000226static void IRQ_check(struct openpic *opp, struct irq_queue *q)
Scott Woodb823f982013-04-12 14:08:43 +0000227{
228 int irq = -1;
229 int next = -1;
230 int priority = -1;
231
232 for (;;) {
233 irq = find_next_bit(q->queue, opp->max_irq, irq + 1);
Scott Woodf0f5c482013-04-12 14:08:45 +0000234 if (irq == opp->max_irq)
Scott Woodb823f982013-04-12 14:08:43 +0000235 break;
Scott Woodb823f982013-04-12 14:08:43 +0000236
Scott Woodf0f5c482013-04-12 14:08:45 +0000237 pr_debug("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n",
Scott Woodb823f982013-04-12 14:08:43 +0000238 irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority);
239
240 if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) {
241 next = irq;
242 priority = IVPR_PRIORITY(opp->src[irq].ivpr);
243 }
244 }
245
246 q->next = next;
247 q->priority = priority;
248}
249
Scott Woodf0f5c482013-04-12 14:08:45 +0000250static int IRQ_get_next(struct openpic *opp, struct irq_queue *q)
Scott Woodb823f982013-04-12 14:08:43 +0000251{
252 /* XXX: optimize */
253 IRQ_check(opp, q);
254
255 return q->next;
256}
257
Scott Woodf0f5c482013-04-12 14:08:45 +0000258static void IRQ_local_pipe(struct openpic *opp, int n_CPU, int n_IRQ,
Scott Woodb823f982013-04-12 14:08:43 +0000259 bool active, bool was_active)
260{
Scott Woodf0f5c482013-04-12 14:08:45 +0000261 struct irq_dest *dst;
262 struct irq_source *src;
Scott Woodb823f982013-04-12 14:08:43 +0000263 int priority;
264
265 dst = &opp->dst[n_CPU];
266 src = &opp->src[n_IRQ];
267
Scott Woodf0f5c482013-04-12 14:08:45 +0000268 pr_debug("%s: IRQ %d active %d was %d\n",
Scott Woodb823f982013-04-12 14:08:43 +0000269 __func__, n_IRQ, active, was_active);
270
271 if (src->output != OPENPIC_OUTPUT_INT) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000272 pr_debug("%s: output %d irq %d active %d was %d count %d\n",
Scott Woodb823f982013-04-12 14:08:43 +0000273 __func__, src->output, n_IRQ, active, was_active,
274 dst->outputs_active[src->output]);
275
276 /* On Freescale MPIC, critical interrupts ignore priority,
277 * IACK, EOI, etc. Before MPIC v4.1 they also ignore
278 * masking.
279 */
280 if (active) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000281 if (!was_active &&
282 dst->outputs_active[src->output]++ == 0) {
283 pr_debug("%s: Raise OpenPIC output %d cpu %d irq %d\n",
284 __func__, src->output, n_CPU, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000285 qemu_irq_raise(dst->irqs[src->output]);
286 }
287 } else {
Scott Woodf0f5c482013-04-12 14:08:45 +0000288 if (was_active &&
289 --dst->outputs_active[src->output] == 0) {
290 pr_debug("%s: Lower OpenPIC output %d cpu %d irq %d\n",
291 __func__, src->output, n_CPU, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000292 qemu_irq_lower(dst->irqs[src->output]);
293 }
294 }
295
296 return;
297 }
298
299 priority = IVPR_PRIORITY(src->ivpr);
300
301 /* Even if the interrupt doesn't have enough priority,
302 * it is still raised, in case ctpr is lowered later.
303 */
Scott Woodf0f5c482013-04-12 14:08:45 +0000304 if (active)
Scott Woodb823f982013-04-12 14:08:43 +0000305 IRQ_setbit(&dst->raised, n_IRQ);
Scott Woodf0f5c482013-04-12 14:08:45 +0000306 else
Scott Woodb823f982013-04-12 14:08:43 +0000307 IRQ_resetbit(&dst->raised, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000308
309 IRQ_check(opp, &dst->raised);
310
311 if (active && priority <= dst->ctpr) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000312 pr_debug("%s: IRQ %d priority %d too low for ctpr %d on CPU %d\n",
313 __func__, n_IRQ, priority, dst->ctpr, n_CPU);
Scott Woodb823f982013-04-12 14:08:43 +0000314 active = 0;
315 }
316
317 if (active) {
318 if (IRQ_get_next(opp, &dst->servicing) >= 0 &&
319 priority <= dst->servicing.priority) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000320 pr_debug("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n",
321 __func__, n_IRQ, dst->servicing.next, n_CPU);
Scott Woodb823f982013-04-12 14:08:43 +0000322 } else {
Scott Woodf0f5c482013-04-12 14:08:45 +0000323 pr_debug("%s: Raise OpenPIC INT output cpu %d irq %d/%d\n",
324 __func__, n_CPU, n_IRQ, dst->raised.next);
Scott Woodb823f982013-04-12 14:08:43 +0000325 qemu_irq_raise(opp->dst[n_CPU].
326 irqs[OPENPIC_OUTPUT_INT]);
327 }
328 } else {
329 IRQ_get_next(opp, &dst->servicing);
330 if (dst->raised.priority > dst->ctpr &&
331 dst->raised.priority > dst->servicing.priority) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000332 pr_debug("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d\n",
333 __func__, n_IRQ, dst->raised.next,
334 dst->raised.priority, dst->ctpr,
335 dst->servicing.priority, n_CPU);
Scott Woodb823f982013-04-12 14:08:43 +0000336 /* IRQ line stays asserted */
337 } else {
Scott Woodf0f5c482013-04-12 14:08:45 +0000338 pr_debug("%s: IRQ %d inactive, current prio %d/%d, CPU %d\n",
339 __func__, n_IRQ, dst->ctpr,
340 dst->servicing.priority, n_CPU);
Scott Woodb823f982013-04-12 14:08:43 +0000341 qemu_irq_lower(opp->dst[n_CPU].
342 irqs[OPENPIC_OUTPUT_INT]);
343 }
344 }
345}
346
347/* update pic state because registers for n_IRQ have changed value */
Scott Woodf0f5c482013-04-12 14:08:45 +0000348static void openpic_update_irq(struct openpic *opp, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000349{
Scott Woodf0f5c482013-04-12 14:08:45 +0000350 struct irq_source *src;
Scott Woodb823f982013-04-12 14:08:43 +0000351 bool active, was_active;
352 int i;
353
354 src = &opp->src[n_IRQ];
355 active = src->pending;
356
357 if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) {
358 /* Interrupt source is disabled */
Scott Woodf0f5c482013-04-12 14:08:45 +0000359 pr_debug("%s: IRQ %d is disabled\n", __func__, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000360 active = false;
361 }
362
Scott Woodf0f5c482013-04-12 14:08:45 +0000363 was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK);
Scott Woodb823f982013-04-12 14:08:43 +0000364
365 /*
366 * We don't have a similar check for already-active because
367 * ctpr may have changed and we need to withdraw the interrupt.
368 */
369 if (!active && !was_active) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000370 pr_debug("%s: IRQ %d is already inactive\n", __func__, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000371 return;
372 }
373
Scott Woodf0f5c482013-04-12 14:08:45 +0000374 if (active)
Scott Woodb823f982013-04-12 14:08:43 +0000375 src->ivpr |= IVPR_ACTIVITY_MASK;
Scott Woodf0f5c482013-04-12 14:08:45 +0000376 else
Scott Woodb823f982013-04-12 14:08:43 +0000377 src->ivpr &= ~IVPR_ACTIVITY_MASK;
Scott Woodb823f982013-04-12 14:08:43 +0000378
379 if (src->destmask == 0) {
380 /* No target */
Scott Woodf0f5c482013-04-12 14:08:45 +0000381 pr_debug("%s: IRQ %d has no target\n", __func__, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000382 return;
383 }
384
385 if (src->destmask == (1 << src->last_cpu)) {
386 /* Only one CPU is allowed to receive this IRQ */
387 IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active);
388 } else if (!(src->ivpr & IVPR_MODE_MASK)) {
389 /* Directed delivery mode */
390 for (i = 0; i < opp->nb_cpus; i++) {
391 if (src->destmask & (1 << i)) {
392 IRQ_local_pipe(opp, i, n_IRQ, active,
393 was_active);
394 }
395 }
396 } else {
397 /* Distributed delivery mode */
398 for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000399 if (i == opp->nb_cpus)
Scott Woodb823f982013-04-12 14:08:43 +0000400 i = 0;
Scott Woodf0f5c482013-04-12 14:08:45 +0000401
Scott Woodb823f982013-04-12 14:08:43 +0000402 if (src->destmask & (1 << i)) {
403 IRQ_local_pipe(opp, i, n_IRQ, active,
404 was_active);
405 src->last_cpu = i;
406 break;
407 }
408 }
409 }
410}
411
412static void openpic_set_irq(void *opaque, int n_IRQ, int level)
413{
Scott Woodf0f5c482013-04-12 14:08:45 +0000414 struct openpic *opp = opaque;
415 struct irq_source *src;
Scott Woodb823f982013-04-12 14:08:43 +0000416
417 if (n_IRQ >= MAX_IRQ) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000418 pr_err("%s: IRQ %d out of range\n", __func__, n_IRQ);
Scott Woodb823f982013-04-12 14:08:43 +0000419 abort();
420 }
421
422 src = &opp->src[n_IRQ];
Scott Woodf0f5c482013-04-12 14:08:45 +0000423 pr_debug("openpic: set irq %d = %d ivpr=0x%08x\n",
Scott Woodb823f982013-04-12 14:08:43 +0000424 n_IRQ, level, src->ivpr);
425 if (src->level) {
426 /* level-sensitive irq */
427 src->pending = level;
428 openpic_update_irq(opp, n_IRQ);
429 } else {
430 /* edge-sensitive irq */
431 if (level) {
432 src->pending = 1;
433 openpic_update_irq(opp, n_IRQ);
434 }
435
436 if (src->output != OPENPIC_OUTPUT_INT) {
437 /* Edge-triggered interrupts shouldn't be used
438 * with non-INT delivery, but just in case,
439 * try to make it do something sane rather than
440 * cause an interrupt storm. This is close to
441 * what you'd probably see happen in real hardware.
442 */
443 src->pending = 0;
444 openpic_update_irq(opp, n_IRQ);
445 }
446 }
447}
448
Scott Woodf0f5c482013-04-12 14:08:45 +0000449static void openpic_reset(DeviceState *d)
Scott Woodb823f982013-04-12 14:08:43 +0000450{
Scott Woodf0f5c482013-04-12 14:08:45 +0000451 struct openpic *opp = FROM_SYSBUS(typeof(*opp), SYS_BUS_DEVICE(d));
Scott Woodb823f982013-04-12 14:08:43 +0000452 int i;
453
454 opp->gcr = GCR_RESET;
455 /* Initialise controller registers */
456 opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) |
457 ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) |
458 (opp->vid << FRR_VID_SHIFT);
459
460 opp->pir = 0;
461 opp->spve = -1 & opp->vector_mask;
462 opp->tfrr = opp->tfrr_reset;
463 /* Initialise IRQ sources */
464 for (i = 0; i < opp->max_irq; i++) {
465 opp->src[i].ivpr = opp->ivpr_reset;
466 opp->src[i].idr = opp->idr_reset;
467
468 switch (opp->src[i].type) {
469 case IRQ_TYPE_NORMAL:
470 opp->src[i].level =
Scott Woodf0f5c482013-04-12 14:08:45 +0000471 !!(opp->ivpr_reset & IVPR_SENSE_MASK);
Scott Woodb823f982013-04-12 14:08:43 +0000472 break;
473
474 case IRQ_TYPE_FSLINT:
475 opp->src[i].ivpr |= IVPR_POLARITY_MASK;
476 break;
477
478 case IRQ_TYPE_FSLSPECIAL:
479 break;
480 }
481 }
482 /* Initialise IRQ destinations */
483 for (i = 0; i < MAX_CPU; i++) {
484 opp->dst[i].ctpr = 15;
Scott Woodf0f5c482013-04-12 14:08:45 +0000485 memset(&opp->dst[i].raised, 0, sizeof(struct irq_queue));
Scott Woodb823f982013-04-12 14:08:43 +0000486 opp->dst[i].raised.next = -1;
Scott Woodf0f5c482013-04-12 14:08:45 +0000487 memset(&opp->dst[i].servicing, 0, sizeof(struct irq_queue));
Scott Woodb823f982013-04-12 14:08:43 +0000488 opp->dst[i].servicing.next = -1;
489 }
490 /* Initialise timers */
491 for (i = 0; i < MAX_TMR; i++) {
492 opp->timers[i].tccr = 0;
493 opp->timers[i].tbcr = TBCR_CI;
494 }
495 /* Go out of RESET state */
496 opp->gcr = 0;
497}
498
Scott Woodf0f5c482013-04-12 14:08:45 +0000499static inline uint32_t read_IRQreg_idr(struct openpic *opp, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000500{
501 return opp->src[n_IRQ].idr;
502}
503
Scott Woodf0f5c482013-04-12 14:08:45 +0000504static inline uint32_t read_IRQreg_ilr(struct openpic *opp, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000505{
Scott Woodf0f5c482013-04-12 14:08:45 +0000506 if (opp->flags & OPENPIC_FLAG_ILR)
Scott Woodb823f982013-04-12 14:08:43 +0000507 return output_to_inttgt(opp->src[n_IRQ].output);
Scott Woodb823f982013-04-12 14:08:43 +0000508
509 return 0xffffffff;
510}
511
Scott Woodf0f5c482013-04-12 14:08:45 +0000512static inline uint32_t read_IRQreg_ivpr(struct openpic *opp, int n_IRQ)
Scott Woodb823f982013-04-12 14:08:43 +0000513{
514 return opp->src[n_IRQ].ivpr;
515}
516
Scott Woodf0f5c482013-04-12 14:08:45 +0000517static inline void write_IRQreg_idr(struct openpic *opp, int n_IRQ,
518 uint32_t val)
Scott Woodb823f982013-04-12 14:08:43 +0000519{
Scott Woodf0f5c482013-04-12 14:08:45 +0000520 struct irq_source *src = &opp->src[n_IRQ];
Scott Woodb823f982013-04-12 14:08:43 +0000521 uint32_t normal_mask = (1UL << opp->nb_cpus) - 1;
522 uint32_t crit_mask = 0;
523 uint32_t mask = normal_mask;
524 int crit_shift = IDR_EP_SHIFT - opp->nb_cpus;
525 int i;
526
527 if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
528 crit_mask = mask << crit_shift;
529 mask |= crit_mask | IDR_EP;
530 }
531
532 src->idr = val & mask;
Scott Woodf0f5c482013-04-12 14:08:45 +0000533 pr_debug("Set IDR %d to 0x%08x\n", n_IRQ, src->idr);
Scott Woodb823f982013-04-12 14:08:43 +0000534
535 if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
536 if (src->idr & crit_mask) {
537 if (src->idr & normal_mask) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000538 pr_debug("%s: IRQ configured for multiple output types, using critical\n",
539 __func__);
Scott Woodb823f982013-04-12 14:08:43 +0000540 }
541
542 src->output = OPENPIC_OUTPUT_CINT;
543 src->nomask = true;
544 src->destmask = 0;
545
546 for (i = 0; i < opp->nb_cpus; i++) {
547 int n_ci = IDR_CI0_SHIFT - i;
548
Scott Woodf0f5c482013-04-12 14:08:45 +0000549 if (src->idr & (1UL << n_ci))
Scott Woodb823f982013-04-12 14:08:43 +0000550 src->destmask |= 1UL << i;
Scott Woodb823f982013-04-12 14:08:43 +0000551 }
552 } else {
553 src->output = OPENPIC_OUTPUT_INT;
554 src->nomask = false;
555 src->destmask = src->idr & normal_mask;
556 }
557 } else {
558 src->destmask = src->idr;
559 }
560}
561
Scott Woodf0f5c482013-04-12 14:08:45 +0000562static inline void write_IRQreg_ilr(struct openpic *opp, int n_IRQ,
563 uint32_t val)
Scott Woodb823f982013-04-12 14:08:43 +0000564{
565 if (opp->flags & OPENPIC_FLAG_ILR) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000566 struct irq_source *src = &opp->src[n_IRQ];
Scott Woodb823f982013-04-12 14:08:43 +0000567
568 src->output = inttgt_to_output(val & ILR_INTTGT_MASK);
Scott Woodf0f5c482013-04-12 14:08:45 +0000569 pr_debug("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr,
Scott Woodb823f982013-04-12 14:08:43 +0000570 src->output);
571
572 /* TODO: on MPIC v4.0 only, set nomask for non-INT */
573 }
574}
575
Scott Woodf0f5c482013-04-12 14:08:45 +0000576static inline void write_IRQreg_ivpr(struct openpic *opp, int n_IRQ,
Scott Woodb823f982013-04-12 14:08:43 +0000577 uint32_t val)
578{
579 uint32_t mask;
580
581 /* NOTE when implementing newer FSL MPIC models: starting with v4.0,
582 * the polarity bit is read-only on internal interrupts.
583 */
584 mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK |
585 IVPR_POLARITY_MASK | opp->vector_mask;
586
587 /* ACTIVITY bit is read-only */
588 opp->src[n_IRQ].ivpr =
589 (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask);
590
591 /* For FSL internal interrupts, The sense bit is reserved and zero,
592 * and the interrupt is always level-triggered. Timers and IPIs
593 * have no sense or polarity bits, and are edge-triggered.
594 */
595 switch (opp->src[n_IRQ].type) {
596 case IRQ_TYPE_NORMAL:
597 opp->src[n_IRQ].level =
Scott Woodf0f5c482013-04-12 14:08:45 +0000598 !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK);
Scott Woodb823f982013-04-12 14:08:43 +0000599 break;
600
601 case IRQ_TYPE_FSLINT:
602 opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK;
603 break;
604
605 case IRQ_TYPE_FSLSPECIAL:
606 opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK);
607 break;
608 }
609
610 openpic_update_irq(opp, n_IRQ);
Scott Woodf0f5c482013-04-12 14:08:45 +0000611 pr_debug("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val,
Scott Woodb823f982013-04-12 14:08:43 +0000612 opp->src[n_IRQ].ivpr);
613}
614
Scott Woodf0f5c482013-04-12 14:08:45 +0000615static void openpic_gcr_write(struct openpic *opp, uint64_t val)
Scott Woodb823f982013-04-12 14:08:43 +0000616{
617 bool mpic_proxy = false;
618
619 if (val & GCR_RESET) {
620 openpic_reset(&opp->busdev.qdev);
621 return;
622 }
623
624 opp->gcr &= ~opp->mpic_mode_mask;
625 opp->gcr |= val & opp->mpic_mode_mask;
626
627 /* Set external proxy mode */
Scott Woodf0f5c482013-04-12 14:08:45 +0000628 if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY)
Scott Woodb823f982013-04-12 14:08:43 +0000629 mpic_proxy = true;
Scott Woodb823f982013-04-12 14:08:43 +0000630
631 ppce500_set_mpic_proxy(mpic_proxy);
632}
633
Scott Woodf0f5c482013-04-12 14:08:45 +0000634static void openpic_gbl_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +0000635 unsigned len)
636{
Scott Woodf0f5c482013-04-12 14:08:45 +0000637 struct openpic *opp = opaque;
638 struct irq_dest *dst;
Scott Woodb823f982013-04-12 14:08:43 +0000639 int idx;
640
Scott Woodf0f5c482013-04-12 14:08:45 +0000641 pr_debug("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n",
Scott Woodb823f982013-04-12 14:08:43 +0000642 __func__, addr, val);
Scott Woodf0f5c482013-04-12 14:08:45 +0000643 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000644 return;
Scott Woodf0f5c482013-04-12 14:08:45 +0000645
Scott Woodb823f982013-04-12 14:08:43 +0000646 switch (addr) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000647 case 0x00: /* Block Revision Register1 (BRR1) is Readonly */
Scott Woodb823f982013-04-12 14:08:43 +0000648 break;
649 case 0x40:
650 case 0x50:
651 case 0x60:
652 case 0x70:
653 case 0x80:
654 case 0x90:
655 case 0xA0:
656 case 0xB0:
657 openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
658 break;
659 case 0x1000: /* FRR */
660 break;
661 case 0x1020: /* GCR */
662 openpic_gcr_write(opp, val);
663 break;
664 case 0x1080: /* VIR */
665 break;
666 case 0x1090: /* PIR */
667 for (idx = 0; idx < opp->nb_cpus; idx++) {
668 if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) {
Scott Woodf0f5c482013-04-12 14:08:45 +0000669 pr_debug("Raise OpenPIC RESET output for CPU %d\n",
670 idx);
Scott Woodb823f982013-04-12 14:08:43 +0000671 dst = &opp->dst[idx];
672 qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]);
Scott Woodf0f5c482013-04-12 14:08:45 +0000673 } else if (!(val & (1 << idx)) &&
674 (opp->pir & (1 << idx))) {
675 pr_debug("Lower OpenPIC RESET output for CPU %d\n",
676 idx);
Scott Woodb823f982013-04-12 14:08:43 +0000677 dst = &opp->dst[idx];
678 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]);
679 }
680 }
681 opp->pir = val;
682 break;
683 case 0x10A0: /* IPI_IVPR */
684 case 0x10B0:
685 case 0x10C0:
Scott Woodf0f5c482013-04-12 14:08:45 +0000686 case 0x10D0: {
687 int idx;
688 idx = (addr - 0x10A0) >> 4;
689 write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val);
Scott Woodb823f982013-04-12 14:08:43 +0000690 break;
Scott Woodf0f5c482013-04-12 14:08:45 +0000691 }
Scott Woodb823f982013-04-12 14:08:43 +0000692 case 0x10E0: /* SPVE */
693 opp->spve = val & opp->vector_mask;
694 break;
695 default:
696 break;
697 }
698}
699
Scott Woodf0f5c482013-04-12 14:08:45 +0000700static uint64_t openpic_gbl_read(void *opaque, gpa_t addr, unsigned len)
Scott Woodb823f982013-04-12 14:08:43 +0000701{
Scott Woodf0f5c482013-04-12 14:08:45 +0000702 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000703 uint32_t retval;
704
Scott Woodf0f5c482013-04-12 14:08:45 +0000705 pr_debug("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
Scott Woodb823f982013-04-12 14:08:43 +0000706 retval = 0xFFFFFFFF;
Scott Woodf0f5c482013-04-12 14:08:45 +0000707 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000708 return retval;
Scott Woodf0f5c482013-04-12 14:08:45 +0000709
Scott Woodb823f982013-04-12 14:08:43 +0000710 switch (addr) {
711 case 0x1000: /* FRR */
712 retval = opp->frr;
713 break;
714 case 0x1020: /* GCR */
715 retval = opp->gcr;
716 break;
717 case 0x1080: /* VIR */
718 retval = opp->vir;
719 break;
720 case 0x1090: /* PIR */
721 retval = 0x00000000;
722 break;
723 case 0x00: /* Block Revision Register1 (BRR1) */
724 retval = opp->brr1;
725 break;
726 case 0x40:
727 case 0x50:
728 case 0x60:
729 case 0x70:
730 case 0x80:
731 case 0x90:
732 case 0xA0:
733 case 0xB0:
734 retval =
735 openpic_cpu_read_internal(opp, addr, get_current_cpu());
736 break;
737 case 0x10A0: /* IPI_IVPR */
738 case 0x10B0:
739 case 0x10C0:
740 case 0x10D0:
741 {
742 int idx;
743 idx = (addr - 0x10A0) >> 4;
744 retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx);
745 }
746 break;
747 case 0x10E0: /* SPVE */
748 retval = opp->spve;
749 break;
750 default:
751 break;
752 }
Scott Woodf0f5c482013-04-12 14:08:45 +0000753 pr_debug("%s: => 0x%08x\n", __func__, retval);
Scott Woodb823f982013-04-12 14:08:43 +0000754
755 return retval;
756}
757
Scott Woodf0f5c482013-04-12 14:08:45 +0000758static void openpic_tmr_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +0000759 unsigned len)
760{
Scott Woodf0f5c482013-04-12 14:08:45 +0000761 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000762 int idx;
763
764 addr += 0x10f0;
765
Scott Woodf0f5c482013-04-12 14:08:45 +0000766 pr_debug("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n",
Scott Woodb823f982013-04-12 14:08:43 +0000767 __func__, addr, val);
Scott Woodf0f5c482013-04-12 14:08:45 +0000768 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000769 return;
Scott Woodb823f982013-04-12 14:08:43 +0000770
771 if (addr == 0x10f0) {
772 /* TFRR */
773 opp->tfrr = val;
774 return;
775 }
776
777 idx = (addr >> 6) & 0x3;
778 addr = addr & 0x30;
779
780 switch (addr & 0x30) {
781 case 0x00: /* TCCR */
782 break;
783 case 0x10: /* TBCR */
784 if ((opp->timers[idx].tccr & TCCR_TOG) != 0 &&
785 (val & TBCR_CI) == 0 &&
Scott Woodf0f5c482013-04-12 14:08:45 +0000786 (opp->timers[idx].tbcr & TBCR_CI) != 0)
Scott Woodb823f982013-04-12 14:08:43 +0000787 opp->timers[idx].tccr &= ~TCCR_TOG;
Scott Woodf0f5c482013-04-12 14:08:45 +0000788
Scott Woodb823f982013-04-12 14:08:43 +0000789 opp->timers[idx].tbcr = val;
790 break;
791 case 0x20: /* TVPR */
792 write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val);
793 break;
794 case 0x30: /* TDR */
795 write_IRQreg_idr(opp, opp->irq_tim0 + idx, val);
796 break;
797 }
798}
799
Scott Woodf0f5c482013-04-12 14:08:45 +0000800static uint64_t openpic_tmr_read(void *opaque, gpa_t addr, unsigned len)
Scott Woodb823f982013-04-12 14:08:43 +0000801{
Scott Woodf0f5c482013-04-12 14:08:45 +0000802 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000803 uint32_t retval = -1;
804 int idx;
805
Scott Woodf0f5c482013-04-12 14:08:45 +0000806 pr_debug("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
807 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000808 goto out;
Scott Woodf0f5c482013-04-12 14:08:45 +0000809
Scott Woodb823f982013-04-12 14:08:43 +0000810 idx = (addr >> 6) & 0x3;
811 if (addr == 0x0) {
812 /* TFRR */
813 retval = opp->tfrr;
814 goto out;
815 }
816 switch (addr & 0x30) {
817 case 0x00: /* TCCR */
818 retval = opp->timers[idx].tccr;
819 break;
820 case 0x10: /* TBCR */
821 retval = opp->timers[idx].tbcr;
822 break;
823 case 0x20: /* TIPV */
824 retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx);
825 break;
826 case 0x30: /* TIDE (TIDR) */
827 retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx);
828 break;
829 }
830
831out:
Scott Woodf0f5c482013-04-12 14:08:45 +0000832 pr_debug("%s: => 0x%08x\n", __func__, retval);
Scott Woodb823f982013-04-12 14:08:43 +0000833
834 return retval;
835}
836
Scott Woodf0f5c482013-04-12 14:08:45 +0000837static void openpic_src_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +0000838 unsigned len)
839{
Scott Woodf0f5c482013-04-12 14:08:45 +0000840 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000841 int idx;
842
Scott Woodf0f5c482013-04-12 14:08:45 +0000843 pr_debug("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n",
Scott Woodb823f982013-04-12 14:08:43 +0000844 __func__, addr, val);
845
846 addr = addr & 0xffff;
847 idx = addr >> 5;
848
849 switch (addr & 0x1f) {
850 case 0x00:
851 write_IRQreg_ivpr(opp, idx, val);
852 break;
853 case 0x10:
854 write_IRQreg_idr(opp, idx, val);
855 break;
856 case 0x18:
857 write_IRQreg_ilr(opp, idx, val);
858 break;
859 }
860}
861
862static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len)
863{
Scott Woodf0f5c482013-04-12 14:08:45 +0000864 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000865 uint32_t retval;
866 int idx;
867
Scott Woodf0f5c482013-04-12 14:08:45 +0000868 pr_debug("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
Scott Woodb823f982013-04-12 14:08:43 +0000869 retval = 0xFFFFFFFF;
870
871 addr = addr & 0xffff;
872 idx = addr >> 5;
873
874 switch (addr & 0x1f) {
875 case 0x00:
876 retval = read_IRQreg_ivpr(opp, idx);
877 break;
878 case 0x10:
879 retval = read_IRQreg_idr(opp, idx);
880 break;
881 case 0x18:
882 retval = read_IRQreg_ilr(opp, idx);
883 break;
884 }
885
Scott Woodf0f5c482013-04-12 14:08:45 +0000886 pr_debug("%s: => 0x%08x\n", __func__, retval);
Scott Woodb823f982013-04-12 14:08:43 +0000887 return retval;
888}
889
Scott Woodf0f5c482013-04-12 14:08:45 +0000890static void openpic_msi_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +0000891 unsigned size)
892{
Scott Woodf0f5c482013-04-12 14:08:45 +0000893 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000894 int idx = opp->irq_msi;
895 int srs, ibs;
896
Scott Woodf0f5c482013-04-12 14:08:45 +0000897 pr_debug("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n",
Scott Woodb823f982013-04-12 14:08:43 +0000898 __func__, addr, val);
Scott Woodf0f5c482013-04-12 14:08:45 +0000899 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000900 return;
Scott Woodb823f982013-04-12 14:08:43 +0000901
902 switch (addr) {
903 case MSIIR_OFFSET:
904 srs = val >> MSIIR_SRS_SHIFT;
905 idx += srs;
906 ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT;
907 opp->msi[srs].msir |= 1 << ibs;
908 openpic_set_irq(opp, idx, 1);
909 break;
910 default:
911 /* most registers are read-only, thus ignored */
912 break;
913 }
914}
915
Scott Woodf0f5c482013-04-12 14:08:45 +0000916static uint64_t openpic_msi_read(void *opaque, gpa_t addr, unsigned size)
Scott Woodb823f982013-04-12 14:08:43 +0000917{
Scott Woodf0f5c482013-04-12 14:08:45 +0000918 struct openpic *opp = opaque;
Scott Woodb823f982013-04-12 14:08:43 +0000919 uint64_t r = 0;
920 int i, srs;
921
Scott Woodf0f5c482013-04-12 14:08:45 +0000922 pr_debug("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
923 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000924 return -1;
Scott Woodb823f982013-04-12 14:08:43 +0000925
926 srs = addr >> 4;
927
928 switch (addr) {
929 case 0x00:
930 case 0x10:
931 case 0x20:
932 case 0x30:
933 case 0x40:
934 case 0x50:
935 case 0x60:
936 case 0x70: /* MSIRs */
937 r = opp->msi[srs].msir;
938 /* Clear on read */
939 opp->msi[srs].msir = 0;
940 openpic_set_irq(opp, opp->irq_msi + srs, 0);
941 break;
942 case 0x120: /* MSISR */
Scott Woodf0f5c482013-04-12 14:08:45 +0000943 for (i = 0; i < MAX_MSI; i++)
Scott Woodb823f982013-04-12 14:08:43 +0000944 r |= (opp->msi[i].msir ? 1 : 0) << i;
Scott Woodb823f982013-04-12 14:08:43 +0000945 break;
946 }
947
948 return r;
949}
950
Scott Woodf0f5c482013-04-12 14:08:45 +0000951static uint64_t openpic_summary_read(void *opaque, gpa_t addr, unsigned size)
Scott Woodb823f982013-04-12 14:08:43 +0000952{
953 uint64_t r = 0;
954
Scott Woodf0f5c482013-04-12 14:08:45 +0000955 pr_debug("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
Scott Woodb823f982013-04-12 14:08:43 +0000956
957 /* TODO: EISR/EIMR */
958
959 return r;
960}
961
Scott Woodf0f5c482013-04-12 14:08:45 +0000962static void openpic_summary_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +0000963 unsigned size)
964{
Scott Woodf0f5c482013-04-12 14:08:45 +0000965 pr_debug("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n",
Scott Woodb823f982013-04-12 14:08:43 +0000966 __func__, addr, val);
967
968 /* TODO: EISR/EIMR */
969}
970
Scott Woodf0f5c482013-04-12 14:08:45 +0000971static void openpic_cpu_write_internal(void *opaque, gpa_t addr,
Scott Woodb823f982013-04-12 14:08:43 +0000972 uint32_t val, int idx)
973{
Scott Woodf0f5c482013-04-12 14:08:45 +0000974 struct openpic *opp = opaque;
975 struct irq_source *src;
976 struct irq_dest *dst;
Scott Woodb823f982013-04-12 14:08:43 +0000977 int s_IRQ, n_IRQ;
978
Scott Woodf0f5c482013-04-12 14:08:45 +0000979 pr_debug("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx,
Scott Woodb823f982013-04-12 14:08:43 +0000980 addr, val);
981
Scott Woodf0f5c482013-04-12 14:08:45 +0000982 if (idx < 0)
Scott Woodb823f982013-04-12 14:08:43 +0000983 return;
Scott Woodb823f982013-04-12 14:08:43 +0000984
Scott Woodf0f5c482013-04-12 14:08:45 +0000985 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +0000986 return;
Scott Woodf0f5c482013-04-12 14:08:45 +0000987
Scott Woodb823f982013-04-12 14:08:43 +0000988 dst = &opp->dst[idx];
989 addr &= 0xFF0;
990 switch (addr) {
991 case 0x40: /* IPIDR */
992 case 0x50:
993 case 0x60:
994 case 0x70:
995 idx = (addr - 0x40) >> 4;
996 /* we use IDE as mask which CPUs to deliver the IPI to still. */
997 opp->src[opp->irq_ipi0 + idx].destmask |= val;
998 openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
999 openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
1000 break;
1001 case 0x80: /* CTPR */
1002 dst->ctpr = val & 0x0000000F;
1003
Scott Woodf0f5c482013-04-12 14:08:45 +00001004 pr_debug("%s: set CPU %d ctpr to %d, raised %d servicing %d\n",
Scott Woodb823f982013-04-12 14:08:43 +00001005 __func__, idx, dst->ctpr, dst->raised.priority,
1006 dst->servicing.priority);
1007
1008 if (dst->raised.priority <= dst->ctpr) {
Scott Woodf0f5c482013-04-12 14:08:45 +00001009 pr_debug("%s: Lower OpenPIC INT output cpu %d due to ctpr\n",
1010 __func__, idx);
Scott Woodb823f982013-04-12 14:08:43 +00001011 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
1012 } else if (dst->raised.priority > dst->servicing.priority) {
Scott Woodf0f5c482013-04-12 14:08:45 +00001013 pr_debug("%s: Raise OpenPIC INT output cpu %d irq %d\n",
Scott Woodb823f982013-04-12 14:08:43 +00001014 __func__, idx, dst->raised.next);
1015 qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
1016 }
1017
1018 break;
1019 case 0x90: /* WHOAMI */
1020 /* Read-only register */
1021 break;
1022 case 0xA0: /* IACK */
1023 /* Read-only register */
1024 break;
1025 case 0xB0: /* EOI */
Scott Woodf0f5c482013-04-12 14:08:45 +00001026 pr_debug("EOI\n");
Scott Woodb823f982013-04-12 14:08:43 +00001027 s_IRQ = IRQ_get_next(opp, &dst->servicing);
1028
1029 if (s_IRQ < 0) {
Scott Woodf0f5c482013-04-12 14:08:45 +00001030 pr_debug("%s: EOI with no interrupt in service\n",
Scott Woodb823f982013-04-12 14:08:43 +00001031 __func__);
1032 break;
1033 }
1034
1035 IRQ_resetbit(&dst->servicing, s_IRQ);
1036 /* Set up next servicing IRQ */
1037 s_IRQ = IRQ_get_next(opp, &dst->servicing);
1038 /* Check queued interrupts. */
1039 n_IRQ = IRQ_get_next(opp, &dst->raised);
1040 src = &opp->src[n_IRQ];
1041 if (n_IRQ != -1 &&
1042 (s_IRQ == -1 ||
1043 IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) {
Scott Woodf0f5c482013-04-12 14:08:45 +00001044 pr_debug("Raise OpenPIC INT output cpu %d irq %d\n",
Scott Woodb823f982013-04-12 14:08:43 +00001045 idx, n_IRQ);
1046 qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]);
1047 }
1048 break;
1049 default:
1050 break;
1051 }
1052}
1053
Scott Woodf0f5c482013-04-12 14:08:45 +00001054static void openpic_cpu_write(void *opaque, gpa_t addr, uint64_t val,
Scott Woodb823f982013-04-12 14:08:43 +00001055 unsigned len)
1056{
1057 openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
1058}
1059
Scott Woodf0f5c482013-04-12 14:08:45 +00001060static uint32_t openpic_iack(struct openpic *opp, struct irq_dest *dst,
1061 int cpu)
Scott Woodb823f982013-04-12 14:08:43 +00001062{
Scott Woodf0f5c482013-04-12 14:08:45 +00001063 struct irq_source *src;
Scott Woodb823f982013-04-12 14:08:43 +00001064 int retval, irq;
1065
Scott Woodf0f5c482013-04-12 14:08:45 +00001066 pr_debug("Lower OpenPIC INT output\n");
Scott Woodb823f982013-04-12 14:08:43 +00001067 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
1068
1069 irq = IRQ_get_next(opp, &dst->raised);
Scott Woodf0f5c482013-04-12 14:08:45 +00001070 pr_debug("IACK: irq=%d\n", irq);
Scott Woodb823f982013-04-12 14:08:43 +00001071
Scott Woodf0f5c482013-04-12 14:08:45 +00001072 if (irq == -1)
Scott Woodb823f982013-04-12 14:08:43 +00001073 /* No more interrupt pending */
1074 return opp->spve;
Scott Woodb823f982013-04-12 14:08:43 +00001075
1076 src = &opp->src[irq];
1077 if (!(src->ivpr & IVPR_ACTIVITY_MASK) ||
1078 !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) {
Scott Woodf0f5c482013-04-12 14:08:45 +00001079 pr_err("%s: bad raised IRQ %d ctpr %d ivpr 0x%08x\n",
Scott Woodb823f982013-04-12 14:08:43 +00001080 __func__, irq, dst->ctpr, src->ivpr);
1081 openpic_update_irq(opp, irq);
1082 retval = opp->spve;
1083 } else {
1084 /* IRQ enter servicing state */
1085 IRQ_setbit(&dst->servicing, irq);
1086 retval = IVPR_VECTOR(opp, src->ivpr);
1087 }
1088
1089 if (!src->level) {
1090 /* edge-sensitive IRQ */
1091 src->ivpr &= ~IVPR_ACTIVITY_MASK;
1092 src->pending = 0;
1093 IRQ_resetbit(&dst->raised, irq);
1094 }
1095
1096 if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + MAX_IPI))) {
1097 src->destmask &= ~(1 << cpu);
1098 if (src->destmask && !src->level) {
1099 /* trigger on CPUs that didn't know about it yet */
1100 openpic_set_irq(opp, irq, 1);
1101 openpic_set_irq(opp, irq, 0);
1102 /* if all CPUs knew about it, set active bit again */
1103 src->ivpr |= IVPR_ACTIVITY_MASK;
1104 }
1105 }
1106
1107 return retval;
1108}
1109
Scott Woodf0f5c482013-04-12 14:08:45 +00001110static uint32_t openpic_cpu_read_internal(void *opaque, gpa_t addr, int idx)
Scott Woodb823f982013-04-12 14:08:43 +00001111{
Scott Woodf0f5c482013-04-12 14:08:45 +00001112 struct openpic *opp = opaque;
1113 struct irq_dest *dst;
Scott Woodb823f982013-04-12 14:08:43 +00001114 uint32_t retval;
1115
Scott Woodf0f5c482013-04-12 14:08:45 +00001116 pr_debug("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr);
Scott Woodb823f982013-04-12 14:08:43 +00001117 retval = 0xFFFFFFFF;
1118
Scott Woodf0f5c482013-04-12 14:08:45 +00001119 if (idx < 0)
Scott Woodb823f982013-04-12 14:08:43 +00001120 return retval;
Scott Woodb823f982013-04-12 14:08:43 +00001121
Scott Woodf0f5c482013-04-12 14:08:45 +00001122 if (addr & 0xF)
Scott Woodb823f982013-04-12 14:08:43 +00001123 return retval;
Scott Woodf0f5c482013-04-12 14:08:45 +00001124
Scott Woodb823f982013-04-12 14:08:43 +00001125 dst = &opp->dst[idx];
1126 addr &= 0xFF0;
1127 switch (addr) {
1128 case 0x80: /* CTPR */
1129 retval = dst->ctpr;
1130 break;
1131 case 0x90: /* WHOAMI */
1132 retval = idx;
1133 break;
1134 case 0xA0: /* IACK */
1135 retval = openpic_iack(opp, dst, idx);
1136 break;
1137 case 0xB0: /* EOI */
1138 retval = 0;
1139 break;
1140 default:
1141 break;
1142 }
Scott Woodf0f5c482013-04-12 14:08:45 +00001143 pr_debug("%s: => 0x%08x\n", __func__, retval);
Scott Woodb823f982013-04-12 14:08:43 +00001144
1145 return retval;
1146}
1147
Scott Woodf0f5c482013-04-12 14:08:45 +00001148static uint64_t openpic_cpu_read(void *opaque, gpa_t addr, unsigned len)
Scott Woodb823f982013-04-12 14:08:43 +00001149{
1150 return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
1151}
1152
Scott Woodf0f5c482013-04-12 14:08:45 +00001153static const struct kvm_io_device_ops openpic_glb_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001154 .write = openpic_gbl_write,
1155 .read = openpic_gbl_read,
Scott Woodb823f982013-04-12 14:08:43 +00001156};
1157
Scott Woodf0f5c482013-04-12 14:08:45 +00001158static const struct kvm_io_device_ops openpic_tmr_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001159 .write = openpic_tmr_write,
1160 .read = openpic_tmr_read,
Scott Woodb823f982013-04-12 14:08:43 +00001161};
1162
Scott Woodf0f5c482013-04-12 14:08:45 +00001163static const struct kvm_io_device_ops openpic_cpu_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001164 .write = openpic_cpu_write,
1165 .read = openpic_cpu_read,
Scott Woodb823f982013-04-12 14:08:43 +00001166};
1167
Scott Woodf0f5c482013-04-12 14:08:45 +00001168static const struct kvm_io_device_ops openpic_src_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001169 .write = openpic_src_write,
1170 .read = openpic_src_read,
Scott Woodb823f982013-04-12 14:08:43 +00001171};
1172
Scott Woodf0f5c482013-04-12 14:08:45 +00001173static const struct kvm_io_device_ops openpic_msi_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001174 .read = openpic_msi_read,
1175 .write = openpic_msi_write,
Scott Woodb823f982013-04-12 14:08:43 +00001176};
1177
Scott Woodf0f5c482013-04-12 14:08:45 +00001178static const struct kvm_io_device_ops openpic_summary_ops_be = {
Scott Woodb823f982013-04-12 14:08:43 +00001179 .read = openpic_summary_read,
1180 .write = openpic_summary_write,
Scott Woodb823f982013-04-12 14:08:43 +00001181};
1182
Scott Woodf0f5c482013-04-12 14:08:45 +00001183struct mem_reg {
Scott Woodb823f982013-04-12 14:08:43 +00001184 const char *name;
Scott Woodf0f5c482013-04-12 14:08:45 +00001185 const struct kvm_io_device_ops *ops;
1186 gpa_t start_addr;
1187 int size;
1188};
Scott Woodb823f982013-04-12 14:08:43 +00001189
Scott Woodf0f5c482013-04-12 14:08:45 +00001190static void fsl_common_init(struct openpic *opp)
Scott Woodb823f982013-04-12 14:08:43 +00001191{
1192 int i;
1193 int virq = MAX_SRC;
1194
1195 opp->vid = VID_REVISION_1_2;
1196 opp->vir = VIR_GENERIC;
1197 opp->vector_mask = 0xFFFF;
1198 opp->tfrr_reset = 0;
1199 opp->ivpr_reset = IVPR_MASK_MASK;
1200 opp->idr_reset = 1 << 0;
1201 opp->max_irq = MAX_IRQ;
1202
1203 opp->irq_ipi0 = virq;
1204 virq += MAX_IPI;
1205 opp->irq_tim0 = virq;
1206 virq += MAX_TMR;
1207
1208 assert(virq <= MAX_IRQ);
1209
1210 opp->irq_msi = 224;
1211
1212 msi_supported = true;
Scott Woodf0f5c482013-04-12 14:08:45 +00001213 for (i = 0; i < opp->fsl->max_ext; i++)
Scott Woodb823f982013-04-12 14:08:43 +00001214 opp->src[i].level = false;
Scott Woodb823f982013-04-12 14:08:43 +00001215
1216 /* Internal interrupts, including message and MSI */
1217 for (i = 16; i < MAX_SRC; i++) {
1218 opp->src[i].type = IRQ_TYPE_FSLINT;
1219 opp->src[i].level = true;
1220 }
1221
1222 /* timers and IPIs */
1223 for (i = MAX_SRC; i < virq; i++) {
1224 opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
1225 opp->src[i].level = false;
1226 }
1227}
1228
Scott Woodf0f5c482013-04-12 14:08:45 +00001229static void map_list(struct openpic *opp, const struct mem_reg *list,
1230 int *count)
Scott Woodb823f982013-04-12 14:08:43 +00001231{
1232 while (list->name) {
1233 assert(*count < ARRAY_SIZE(opp->sub_io_mem));
1234
1235 memory_region_init_io(&opp->sub_io_mem[*count], list->ops, opp,
1236 list->name, list->size);
1237
1238 memory_region_add_subregion(&opp->mem, list->start_addr,
1239 &opp->sub_io_mem[*count]);
1240
1241 (*count)++;
1242 list++;
1243 }
1244}
1245
Scott Woodf0f5c482013-04-12 14:08:45 +00001246static int openpic_init(SysBusDevice *dev)
Scott Woodb823f982013-04-12 14:08:43 +00001247{
Scott Woodf0f5c482013-04-12 14:08:45 +00001248 struct openpic *opp = FROM_SYSBUS(typeof(*opp), dev);
Scott Woodb823f982013-04-12 14:08:43 +00001249 int i, j;
1250 int list_count = 0;
Scott Woodf0f5c482013-04-12 14:08:45 +00001251 static const struct mem_reg list_le[] = {
Scott Woodb823f982013-04-12 14:08:43 +00001252 {"glb", &openpic_glb_ops_le,
1253 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
1254 {"tmr", &openpic_tmr_ops_le,
1255 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
1256 {"src", &openpic_src_ops_le,
1257 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
1258 {"cpu", &openpic_cpu_ops_le,
1259 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
1260 {NULL}
1261 };
Scott Woodf0f5c482013-04-12 14:08:45 +00001262 static const struct mem_reg list_be[] = {
Scott Woodb823f982013-04-12 14:08:43 +00001263 {"glb", &openpic_glb_ops_be,
1264 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
1265 {"tmr", &openpic_tmr_ops_be,
1266 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
1267 {"src", &openpic_src_ops_be,
1268 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
1269 {"cpu", &openpic_cpu_ops_be,
1270 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
1271 {NULL}
1272 };
Scott Woodf0f5c482013-04-12 14:08:45 +00001273 static const struct mem_reg list_fsl[] = {
Scott Woodb823f982013-04-12 14:08:43 +00001274 {"msi", &openpic_msi_ops_be,
1275 OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
1276 {"summary", &openpic_summary_ops_be,
1277 OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE},
1278 {NULL}
1279 };
1280
1281 memory_region_init(&opp->mem, "openpic", 0x40000);
1282
1283 switch (opp->model) {
1284 case OPENPIC_MODEL_FSL_MPIC_20:
1285 default:
1286 opp->fsl = &fsl_mpic_20;
1287 opp->brr1 = 0x00400200;
1288 opp->flags |= OPENPIC_FLAG_IDR_CRIT;
1289 opp->nb_irqs = 80;
1290 opp->mpic_mode_mask = GCR_MODE_MIXED;
1291
1292 fsl_common_init(opp);
1293 map_list(opp, list_be, &list_count);
1294 map_list(opp, list_fsl, &list_count);
1295
1296 break;
1297
1298 case OPENPIC_MODEL_FSL_MPIC_42:
1299 opp->fsl = &fsl_mpic_42;
1300 opp->brr1 = 0x00400402;
1301 opp->flags |= OPENPIC_FLAG_ILR;
1302 opp->nb_irqs = 196;
1303 opp->mpic_mode_mask = GCR_MODE_PROXY;
1304
1305 fsl_common_init(opp);
1306 map_list(opp, list_be, &list_count);
1307 map_list(opp, list_fsl, &list_count);
1308
1309 break;
Scott Woodb823f982013-04-12 14:08:43 +00001310 }
1311
Scott Woodb823f982013-04-12 14:08:43 +00001312 return 0;
1313}