Statistics
| Branch: | Revision:

root / hw / grlib_irqmp.c @ 6b331efb

History | View | Annotate | Download (9.3 kB)

1
/*
2
 * QEMU GRLIB IRQMP Emulator
3
 *
4
 * (Multiprocessor and extended interrupt not supported)
5
 *
6
 * Copyright (c) 2010-2011 AdaCore
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26

    
27
#include "sysbus.h"
28
#include "cpu.h"
29

    
30
#include "grlib.h"
31

    
32
#include "trace.h"
33

    
34
#define IRQMP_MAX_CPU 16
35
#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
36

    
37
/* Memory mapped register offsets */
38
#define LEVEL_OFFSET     0x00
39
#define PENDING_OFFSET   0x04
40
#define FORCE0_OFFSET    0x08
41
#define CLEAR_OFFSET     0x0C
42
#define MP_STATUS_OFFSET 0x10
43
#define BROADCAST_OFFSET 0x14
44
#define MASK_OFFSET      0x40
45
#define FORCE_OFFSET     0x80
46
#define EXTENDED_OFFSET  0xC0
47

    
48
typedef struct IRQMPState IRQMPState;
49

    
50
typedef struct IRQMP {
51
    SysBusDevice busdev;
52

    
53
    void *set_pil_in;
54
    void *set_pil_in_opaque;
55

    
56
    IRQMPState *state;
57
} IRQMP;
58

    
59
struct IRQMPState {
60
    uint32_t level;
61
    uint32_t pending;
62
    uint32_t clear;
63
    uint32_t broadcast;
64

    
65
    uint32_t mask[IRQMP_MAX_CPU];
66
    uint32_t force[IRQMP_MAX_CPU];
67
    uint32_t extended[IRQMP_MAX_CPU];
68

    
69
    IRQMP    *parent;
70
};
71

    
72
static void grlib_irqmp_check_irqs(IRQMPState *state)
73
{
74
    uint32_t      pend   = 0;
75
    uint32_t      level0 = 0;
76
    uint32_t      level1 = 0;
77
    set_pil_in_fn set_pil_in;
78

    
79
    assert(state != NULL);
80
    assert(state->parent != NULL);
81

    
82
    /* IRQ for CPU 0 (no SMP support) */
83
    pend = (state->pending | state->force[0])
84
        & state->mask[0];
85

    
86
    level0 = pend & ~state->level;
87
    level1 = pend &  state->level;
88

    
89
    trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
90
                                 state->mask[0], level1, level0);
91

    
92
    set_pil_in = (set_pil_in_fn)state->parent->set_pil_in;
93

    
94
    /* Trigger level1 interrupt first and level0 if there is no level1 */
95
    if (level1 != 0) {
96
        set_pil_in(state->parent->set_pil_in_opaque, level1);
97
    } else {
98
        set_pil_in(state->parent->set_pil_in_opaque, level0);
99
    }
100
}
101

    
102
void grlib_irqmp_ack(DeviceState *dev, int intno)
103
{
104
    SysBusDevice *sdev;
105
    IRQMP        *irqmp;
106
    IRQMPState   *state;
107
    uint32_t      mask;
108

    
109
    assert(dev != NULL);
110

    
111
    sdev = sysbus_from_qdev(dev);
112
    assert(sdev != NULL);
113

    
114
    irqmp = FROM_SYSBUS(typeof(*irqmp), sdev);
115
    assert(irqmp != NULL);
116

    
117
    state = irqmp->state;
118
    assert(state != NULL);
119

    
120
    intno &= 15;
121
    mask = 1 << intno;
122

    
123
    trace_grlib_irqmp_ack(intno);
124

    
125
    /* Clear registers */
126
    state->pending  &= ~mask;
127
    state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
128

    
129
    grlib_irqmp_check_irqs(state);
130
}
131

    
132
void grlib_irqmp_set_irq(void *opaque, int irq, int level)
133
{
134
    IRQMP      *irqmp;
135
    IRQMPState *s;
136
    int         i = 0;
137

    
138
    assert(opaque != NULL);
139

    
140
    irqmp = FROM_SYSBUS(typeof(*irqmp), sysbus_from_qdev(opaque));
141
    assert(irqmp != NULL);
142

    
143
    s = irqmp->state;
144
    assert(s         != NULL);
145
    assert(s->parent != NULL);
146

    
147

    
148
    if (level) {
149
        trace_grlib_irqmp_set_irq(irq);
150

    
151
        if (s->broadcast & 1 << irq) {
152
            /* Broadcasted IRQ */
153
            for (i = 0; i < IRQMP_MAX_CPU; i++) {
154
                s->force[i] |= 1 << irq;
155
            }
156
        } else {
157
            s->pending |= 1 << irq;
158
        }
159
        grlib_irqmp_check_irqs(s);
160

    
161
    }
162
}
163

    
164
static uint32_t grlib_irqmp_readl(void *opaque, target_phys_addr_t addr)
165
{
166
    IRQMP      *irqmp = opaque;
167
    IRQMPState *state;
168

    
169
    assert(irqmp != NULL);
170
    state = irqmp->state;
171
    assert(state != NULL);
172

    
173
    addr &= 0xff;
174

    
175
    /* global registers */
176
    switch (addr) {
177
    case LEVEL_OFFSET:
178
        return state->level;
179

    
180
    case PENDING_OFFSET:
181
        return state->pending;
182

    
183
    case FORCE0_OFFSET:
184
        /* This register is an "alias" for the force register of CPU 0 */
185
        return state->force[0];
186

    
187
    case CLEAR_OFFSET:
188
    case MP_STATUS_OFFSET:
189
        /* Always read as 0 */
190
        return 0;
191

    
192
    case BROADCAST_OFFSET:
193
        return state->broadcast;
194

    
195
    default:
196
        break;
197
    }
198

    
199
    /* mask registers */
200
    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
201
        int cpu = (addr - MASK_OFFSET) / 4;
202
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
203

    
204
        return state->mask[cpu];
205
    }
206

    
207
    /* force registers */
208
    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
209
        int cpu = (addr - FORCE_OFFSET) / 4;
210
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
211

    
212
        return state->force[cpu];
213
    }
214

    
215
    /* extended (not supported) */
216
    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
217
        int cpu = (addr - EXTENDED_OFFSET) / 4;
218
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
219

    
220
        return state->extended[cpu];
221
    }
222

    
223
    trace_grlib_irqmp_unknown_register("read", addr);
224
    return 0;
225
}
226

    
227
static void
228
grlib_irqmp_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
229
{
230
    IRQMP      *irqmp = opaque;
231
    IRQMPState *state;
232

    
233
    assert(irqmp != NULL);
234
    state = irqmp->state;
235
    assert(state != NULL);
236

    
237
    addr &= 0xff;
238

    
239
    /* global registers */
240
    switch (addr) {
241
    case LEVEL_OFFSET:
242
        value &= 0xFFFF << 1; /* clean up the value */
243
        state->level = value;
244
        return;
245

    
246
    case PENDING_OFFSET:
247
        /* Read Only */
248
        return;
249

    
250
    case FORCE0_OFFSET:
251
        /* This register is an "alias" for the force register of CPU 0 */
252

    
253
        value &= 0xFFFE; /* clean up the value */
254
        state->force[0] = value;
255
        grlib_irqmp_check_irqs(irqmp->state);
256
        return;
257

    
258
    case CLEAR_OFFSET:
259
        value &= ~1; /* clean up the value */
260
        state->pending &= ~value;
261
        return;
262

    
263
    case MP_STATUS_OFFSET:
264
        /* Read Only (no SMP support) */
265
        return;
266

    
267
    case BROADCAST_OFFSET:
268
        value &= 0xFFFE; /* clean up the value */
269
        state->broadcast = value;
270
        return;
271

    
272
    default:
273
        break;
274
    }
275

    
276
    /* mask registers */
277
    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
278
        int cpu = (addr - MASK_OFFSET) / 4;
279
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
280

    
281
        value &= ~1; /* clean up the value */
282
        state->mask[cpu] = value;
283
        grlib_irqmp_check_irqs(irqmp->state);
284
        return;
285
    }
286

    
287
    /* force registers */
288
    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
289
        int cpu = (addr - FORCE_OFFSET) / 4;
290
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
291

    
292
        uint32_t force = value & 0xFFFE;
293
        uint32_t clear = (value >> 16) & 0xFFFE;
294
        uint32_t old   = state->force[cpu];
295

    
296
        state->force[cpu] = (old | force) & ~clear;
297
        grlib_irqmp_check_irqs(irqmp->state);
298
        return;
299
    }
300

    
301
    /* extended (not supported) */
302
    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
303
        int cpu = (addr - EXTENDED_OFFSET) / 4;
304
        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
305

    
306
        value &= 0xF; /* clean up the value */
307
        state->extended[cpu] = value;
308
        return;
309
    }
310

    
311
    trace_grlib_irqmp_unknown_register("write", addr);
312
}
313

    
314
static CPUReadMemoryFunc * const grlib_irqmp_read[] = {
315
    NULL, NULL, &grlib_irqmp_readl,
316
};
317

    
318
static CPUWriteMemoryFunc * const grlib_irqmp_write[] = {
319
    NULL, NULL, &grlib_irqmp_writel,
320
};
321

    
322
static void grlib_irqmp_reset(DeviceState *d)
323
{
324
    IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev);
325
    assert(irqmp        != NULL);
326
    assert(irqmp->state != NULL);
327

    
328
    memset(irqmp->state, 0, sizeof *irqmp->state);
329
    irqmp->state->parent = irqmp;
330
}
331

    
332
static int grlib_irqmp_init(SysBusDevice *dev)
333
{
334
    IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev);
335
    int    irqmp_regs;
336

    
337
    assert(irqmp != NULL);
338

    
339
    /* Check parameters */
340
    if (irqmp->set_pil_in == NULL) {
341
        return -1;
342
    }
343

    
344
    irqmp_regs = cpu_register_io_memory(grlib_irqmp_read,
345
                                        grlib_irqmp_write,
346
                                        irqmp, DEVICE_NATIVE_ENDIAN);
347

    
348
    irqmp->state = qemu_mallocz(sizeof *irqmp->state);
349

    
350
    if (irqmp_regs < 0) {
351
        return -1;
352
    }
353

    
354
    sysbus_init_mmio(dev, IRQMP_REG_SIZE, irqmp_regs);
355

    
356
    return 0;
357
}
358

    
359
static SysBusDeviceInfo grlib_irqmp_info = {
360
    .init = grlib_irqmp_init,
361
    .qdev.name  = "grlib,irqmp",
362
    .qdev.reset = grlib_irqmp_reset,
363
    .qdev.size  = sizeof(IRQMP),
364
    .qdev.props = (Property[]) {
365
        DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in),
366
        DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque),
367
        DEFINE_PROP_END_OF_LIST(),
368
    }
369
};
370

    
371
static void grlib_irqmp_register(void)
372
{
373
    sysbus_register_withprop(&grlib_irqmp_info);
374
}
375

    
376
device_init(grlib_irqmp_register)