Statistics
| Branch: | Revision:

root / hw / grlib_irqmp.c @ a8170e5e

History | View | Annotate | Download (9.5 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
    MemoryRegion iomem;
53

    
54
    void *set_pil_in;
55
    void *set_pil_in_opaque;
56

    
57
    IRQMPState *state;
58
} IRQMP;
59

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

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

    
70
    IRQMP    *parent;
71
};
72

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

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

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

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

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

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

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

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

    
110
    assert(dev != NULL);
111

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

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

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

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

    
124
    trace_grlib_irqmp_ack(intno);
125

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

    
130
    grlib_irqmp_check_irqs(state);
131
}
132

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

    
139
    assert(opaque != NULL);
140

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

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

    
148

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

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

    
162
    }
163
}
164

    
165
static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
166
                                 unsigned size)
167
{
168
    IRQMP      *irqmp = opaque;
169
    IRQMPState *state;
170

    
171
    assert(irqmp != NULL);
172
    state = irqmp->state;
173
    assert(state != NULL);
174

    
175
    addr &= 0xff;
176

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

    
182
    case PENDING_OFFSET:
183
        return state->pending;
184

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

    
189
    case CLEAR_OFFSET:
190
    case MP_STATUS_OFFSET:
191
        /* Always read as 0 */
192
        return 0;
193

    
194
    case BROADCAST_OFFSET:
195
        return state->broadcast;
196

    
197
    default:
198
        break;
199
    }
200

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

    
206
        return state->mask[cpu];
207
    }
208

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

    
214
        return state->force[cpu];
215
    }
216

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

    
222
        return state->extended[cpu];
223
    }
224

    
225
    trace_grlib_irqmp_readl_unknown(addr);
226
    return 0;
227
}
228

    
229
static void grlib_irqmp_write(void *opaque, hwaddr addr,
230
                              uint64_t value, unsigned size)
231
{
232
    IRQMP      *irqmp = opaque;
233
    IRQMPState *state;
234

    
235
    assert(irqmp != NULL);
236
    state = irqmp->state;
237
    assert(state != NULL);
238

    
239
    addr &= 0xff;
240

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

    
248
    case PENDING_OFFSET:
249
        /* Read Only */
250
        return;
251

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

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

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

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

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

    
274
    default:
275
        break;
276
    }
277

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

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

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

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

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

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

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

    
313
    trace_grlib_irqmp_writel_unknown(addr, value);
314
}
315

    
316
static const MemoryRegionOps grlib_irqmp_ops = {
317
    .read = grlib_irqmp_read,
318
    .write = grlib_irqmp_write,
319
    .endianness = DEVICE_NATIVE_ENDIAN,
320
    .valid = {
321
        .min_access_size = 4,
322
        .max_access_size = 4,
323
    },
324
};
325

    
326
static void grlib_irqmp_reset(DeviceState *d)
327
{
328
    IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev);
329
    assert(irqmp        != NULL);
330
    assert(irqmp->state != NULL);
331

    
332
    memset(irqmp->state, 0, sizeof *irqmp->state);
333
    irqmp->state->parent = irqmp;
334
}
335

    
336
static int grlib_irqmp_init(SysBusDevice *dev)
337
{
338
    IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev);
339

    
340
    assert(irqmp != NULL);
341

    
342
    /* Check parameters */
343
    if (irqmp->set_pil_in == NULL) {
344
        return -1;
345
    }
346

    
347
    memory_region_init_io(&irqmp->iomem, &grlib_irqmp_ops, irqmp,
348
                          "irqmp", IRQMP_REG_SIZE);
349

    
350
    irqmp->state = g_malloc0(sizeof *irqmp->state);
351

    
352
    sysbus_init_mmio(dev, &irqmp->iomem);
353

    
354
    return 0;
355
}
356

    
357
static Property grlib_irqmp_properties[] = {
358
    DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in),
359
    DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque),
360
    DEFINE_PROP_END_OF_LIST(),
361
};
362

    
363
static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
364
{
365
    DeviceClass *dc = DEVICE_CLASS(klass);
366
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
367

    
368
    k->init = grlib_irqmp_init;
369
    dc->reset = grlib_irqmp_reset;
370
    dc->props = grlib_irqmp_properties;
371
}
372

    
373
static TypeInfo grlib_irqmp_info = {
374
    .name          = "grlib,irqmp",
375
    .parent        = TYPE_SYS_BUS_DEVICE,
376
    .instance_size = sizeof(IRQMP),
377
    .class_init    = grlib_irqmp_class_init,
378
};
379

    
380
static void grlib_irqmp_register_types(void)
381
{
382
    type_register_static(&grlib_irqmp_info);
383
}
384

    
385
type_init(grlib_irqmp_register_types)