Statistics
| Branch: | Revision:

root / hw / exynos4210_combiner.c @ 0d09e41a

History | View | Annotate | Download (15.1 kB)

1
/*
2
 * Samsung exynos4210 Interrupt Combiner
3
 *
4
 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
5
 * All rights reserved.
6
 *
7
 * Evgeny Voevodin <e.voevodin@samsung.com>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU General Public License as published by the
11
 * Free Software Foundation; either version 2 of the License, or (at your
12
 * option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
 * See the GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License along
20
 * with this program; if not, see <http://www.gnu.org/licenses/>.
21
 */
22

    
23
/*
24
 * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
25
 * IRQ sources into groups and provides signal output to GIC from each group. It
26
 * is driven by common mask and enable/disable logic. Take a note that not all
27
 * IRQs are passed to GIC through Combiner.
28
 */
29

    
30
#include "hw/sysbus.h"
31

    
32
#include "hw/arm/exynos4210.h"
33

    
34
//#define DEBUG_COMBINER
35

    
36
#ifdef DEBUG_COMBINER
37
#define DPRINTF(fmt, ...) \
38
        do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
39
                ## __VA_ARGS__); } while (0)
40
#else
41
#define DPRINTF(fmt, ...) do {} while (0)
42
#endif
43

    
44
#define    IIC_NGRP        64            /* Internal Interrupt Combiner
45
                                            Groups number */
46
#define    IIC_NIRQ        (IIC_NGRP * 8)/* Internal Interrupt Combiner
47
                                            Interrupts number */
48
#define IIC_REGION_SIZE    0x108         /* Size of memory mapped region */
49
#define IIC_REGSET_SIZE    0x41
50

    
51
/*
52
 * State for each output signal of internal combiner
53
 */
54
typedef struct CombinerGroupState {
55
    uint8_t src_mask;            /* 1 - source enabled, 0 - disabled */
56
    uint8_t src_pending;        /* Pending source interrupts before masking */
57
} CombinerGroupState;
58

    
59
typedef struct Exynos4210CombinerState {
60
    SysBusDevice busdev;
61
    MemoryRegion iomem;
62

    
63
    struct CombinerGroupState group[IIC_NGRP];
64
    uint32_t reg_set[IIC_REGSET_SIZE];
65
    uint32_t icipsr[2];
66
    uint32_t external;          /* 1 means that this combiner is external */
67

    
68
    qemu_irq output_irq[IIC_NGRP];
69
} Exynos4210CombinerState;
70

    
71
static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
72
    .name = "exynos4210.combiner.groupstate",
73
    .version_id = 1,
74
    .minimum_version_id = 1,
75
    .minimum_version_id_old = 1,
76
    .fields = (VMStateField[]) {
77
        VMSTATE_UINT8(src_mask, CombinerGroupState),
78
        VMSTATE_UINT8(src_pending, CombinerGroupState),
79
        VMSTATE_END_OF_LIST()
80
    }
81
};
82

    
83
static const VMStateDescription vmstate_exynos4210_combiner = {
84
    .name = "exynos4210.combiner",
85
    .version_id = 1,
86
    .minimum_version_id = 1,
87
    .minimum_version_id_old = 1,
88
    .fields = (VMStateField[]) {
89
        VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
90
                vmstate_exynos4210_combiner_group_state, CombinerGroupState),
91
        VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
92
                IIC_REGSET_SIZE),
93
        VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
94
        VMSTATE_UINT32(external, Exynos4210CombinerState),
95
        VMSTATE_END_OF_LIST()
96
    }
97
};
98

    
99
/*
100
 * Get Combiner input GPIO into irqs structure
101
 */
102
void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
103
        int ext)
104
{
105
    int n;
106
    int bit;
107
    int max;
108
    qemu_irq *irq;
109

    
110
    max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
111
        EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
112
    irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
113

    
114
    /*
115
     * Some IRQs of Int/External Combiner are going to two Combiners groups,
116
     * so let split them.
117
     */
118
    for (n = 0; n < max; n++) {
119

    
120
        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
121

    
122
        switch (n) {
123
        /* MDNIE_LCD1 INTG1 */
124
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
125
             EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
126
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
127
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
128
            continue;
129

    
130
        /* TMU INTG3 */
131
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
132
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
133
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
134
            continue;
135

    
136
        /* LCD1 INTG12 */
137
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
138
             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
139
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
140
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
141
            continue;
142

    
143
        /* Multi-Core Timer INTG12 */
144
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
145
             EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
146
               irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
147
                       irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
148
            continue;
149

    
150
        /* Multi-Core Timer INTG35 */
151
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
152
             EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
153
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
154
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
155
            continue;
156

    
157
        /* Multi-Core Timer INTG51 */
158
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
159
             EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
160
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
161
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
162
            continue;
163

    
164
        /* Multi-Core Timer INTG53 */
165
        case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
166
             EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
167
            irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
168
                    irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
169
            continue;
170
        }
171

    
172
        irq[n] = qdev_get_gpio_in(dev, n);
173
    }
174
}
175

    
176
static uint64_t
177
exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size)
178
{
179
    struct Exynos4210CombinerState *s =
180
            (struct Exynos4210CombinerState *)opaque;
181
    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
182
                                   get a start of corresponding group quad */
183
    uint32_t grp_quad_base_n;    /* Base of group quad */
184
    uint32_t reg_n;              /* Register number inside the quad */
185
    uint32_t val;
186

    
187
    req_quad_base_n = offset >> 4;
188
    grp_quad_base_n = req_quad_base_n << 2;
189
    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
190

    
191
    if (req_quad_base_n >= IIC_NGRP) {
192
        /* Read of ICIPSR register */
193
        return s->icipsr[reg_n];
194
    }
195

    
196
    val = 0;
197

    
198
    switch (reg_n) {
199
    /* IISTR */
200
    case 2:
201
        val |= s->group[grp_quad_base_n].src_pending;
202
        val |= s->group[grp_quad_base_n + 1].src_pending << 8;
203
        val |= s->group[grp_quad_base_n + 2].src_pending << 16;
204
        val |= s->group[grp_quad_base_n + 3].src_pending << 24;
205
        break;
206
    /* IIMSR */
207
    case 3:
208
        val |= s->group[grp_quad_base_n].src_mask &
209
        s->group[grp_quad_base_n].src_pending;
210
        val |= (s->group[grp_quad_base_n + 1].src_mask &
211
                s->group[grp_quad_base_n + 1].src_pending) << 8;
212
        val |= (s->group[grp_quad_base_n + 2].src_mask &
213
                s->group[grp_quad_base_n + 2].src_pending) << 16;
214
        val |= (s->group[grp_quad_base_n + 3].src_mask &
215
                s->group[grp_quad_base_n + 3].src_pending) << 24;
216
        break;
217
    default:
218
        if (offset >> 2 >= IIC_REGSET_SIZE) {
219
            hw_error("exynos4210.combiner: overflow of reg_set by 0x"
220
                    TARGET_FMT_plx "offset\n", offset);
221
        }
222
        val = s->reg_set[offset >> 2];
223
        return 0;
224
    }
225
    return val;
226
}
227

    
228
static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
229
{
230
    struct Exynos4210CombinerState *s =
231
            (struct Exynos4210CombinerState *)opaque;
232

    
233
    /* Send interrupt if needed */
234
    if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
235
#ifdef DEBUG_COMBINER
236
        if (group_n != 26) {
237
            /* skip uart */
238
            DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
239
        }
240
#endif
241

    
242
        /* Set Combiner interrupt pending status after masking */
243
        if (group_n >= 32) {
244
            s->icipsr[1] |= 1 << (group_n - 32);
245
        } else {
246
            s->icipsr[0] |= 1 << group_n;
247
        }
248

    
249
        qemu_irq_raise(s->output_irq[group_n]);
250
    } else {
251
#ifdef DEBUG_COMBINER
252
        if (group_n != 26) {
253
            /* skip uart */
254
            DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
255
        }
256
#endif
257

    
258
        /* Set Combiner interrupt pending status after masking */
259
        if (group_n >= 32) {
260
            s->icipsr[1] &= ~(1 << (group_n - 32));
261
        } else {
262
            s->icipsr[0] &= ~(1 << group_n);
263
        }
264

    
265
        qemu_irq_lower(s->output_irq[group_n]);
266
    }
267
}
268

    
269
static void exynos4210_combiner_write(void *opaque, hwaddr offset,
270
        uint64_t val, unsigned size)
271
{
272
    struct Exynos4210CombinerState *s =
273
            (struct Exynos4210CombinerState *)opaque;
274
    uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
275
                                   get a start of corresponding group quad */
276
    uint32_t grp_quad_base_n;    /* Base of group quad */
277
    uint32_t reg_n;              /* Register number inside the quad */
278

    
279
    req_quad_base_n = offset >> 4;
280
    grp_quad_base_n = req_quad_base_n << 2;
281
    reg_n = (offset - (req_quad_base_n << 4)) >> 2;
282

    
283
    if (req_quad_base_n >= IIC_NGRP) {
284
        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
285
                TARGET_FMT_plx "\n", offset);
286
        return;
287
    }
288

    
289
    if (reg_n > 1) {
290
        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
291
                TARGET_FMT_plx "\n", offset);
292
        return;
293
    }
294

    
295
    if (offset >> 2 >= IIC_REGSET_SIZE) {
296
        hw_error("exynos4210.combiner: overflow of reg_set by 0x"
297
                TARGET_FMT_plx "offset\n", offset);
298
    }
299
    s->reg_set[offset >> 2] = val;
300

    
301
    switch (reg_n) {
302
    /* IIESR */
303
    case 0:
304
        /* FIXME: what if irq is pending, allowed by mask, and we allow it
305
         * again. Interrupt will rise again! */
306

    
307
        DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
308
                s->external ? "EXT" : "INT",
309
                grp_quad_base_n,
310
                grp_quad_base_n + 1,
311
                grp_quad_base_n + 2,
312
                grp_quad_base_n + 3);
313

    
314
        /* Enable interrupt sources */
315
        s->group[grp_quad_base_n].src_mask |= val & 0xFF;
316
        s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
317
        s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
318
        s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
319

    
320
        exynos4210_combiner_update(s, grp_quad_base_n);
321
        exynos4210_combiner_update(s, grp_quad_base_n + 1);
322
        exynos4210_combiner_update(s, grp_quad_base_n + 2);
323
        exynos4210_combiner_update(s, grp_quad_base_n + 3);
324
        break;
325
        /* IIECR */
326
    case 1:
327
        DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
328
                s->external ? "EXT" : "INT",
329
                grp_quad_base_n,
330
                grp_quad_base_n + 1,
331
                grp_quad_base_n + 2,
332
                grp_quad_base_n + 3);
333

    
334
        /* Disable interrupt sources */
335
        s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
336
        s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
337
        s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
338
        s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
339

    
340
        exynos4210_combiner_update(s, grp_quad_base_n);
341
        exynos4210_combiner_update(s, grp_quad_base_n + 1);
342
        exynos4210_combiner_update(s, grp_quad_base_n + 2);
343
        exynos4210_combiner_update(s, grp_quad_base_n + 3);
344
        break;
345
    default:
346
        hw_error("exynos4210.combiner: unallowed write access at offset 0x"
347
                TARGET_FMT_plx "\n", offset);
348
        break;
349
    }
350
}
351

    
352
/* Get combiner group and bit from irq number */
353
static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
354
{
355
    *bit = irq - ((irq >> 3) << 3);
356
    return irq >> 3;
357
}
358

    
359
/* Process a change in an external IRQ input.  */
360
static void exynos4210_combiner_handler(void *opaque, int irq, int level)
361
{
362
    struct Exynos4210CombinerState *s =
363
            (struct Exynos4210CombinerState *)opaque;
364
    uint8_t bit_n, group_n;
365

    
366
    group_n = get_combiner_group_and_bit(irq, &bit_n);
367

    
368
    if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
369
        DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
370
                , group_n);
371
        return;
372
    }
373

    
374
    if (level) {
375
        s->group[group_n].src_pending |= 1 << bit_n;
376
    } else {
377
        s->group[group_n].src_pending &= ~(1 << bit_n);
378
    }
379

    
380
    exynos4210_combiner_update(s, group_n);
381
}
382

    
383
static void exynos4210_combiner_reset(DeviceState *d)
384
{
385
    struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
386

    
387
    memset(&s->group, 0, sizeof(s->group));
388
    memset(&s->reg_set, 0, sizeof(s->reg_set));
389

    
390
    s->reg_set[0xC0 >> 2] = 0x01010101;
391
    s->reg_set[0xC4 >> 2] = 0x01010101;
392
    s->reg_set[0xD0 >> 2] = 0x01010101;
393
    s->reg_set[0xD4 >> 2] = 0x01010101;
394
}
395

    
396
static const MemoryRegionOps exynos4210_combiner_ops = {
397
    .read = exynos4210_combiner_read,
398
    .write = exynos4210_combiner_write,
399
    .endianness = DEVICE_NATIVE_ENDIAN,
400
};
401

    
402
/*
403
 * Internal Combiner initialization.
404
 */
405
static int exynos4210_combiner_init(SysBusDevice *dev)
406
{
407
    unsigned int i;
408
    struct Exynos4210CombinerState *s =
409
            FROM_SYSBUS(struct Exynos4210CombinerState, dev);
410

    
411
    /* Allocate general purpose input signals and connect a handler to each of
412
     * them */
413
    qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
414

    
415
    /* Connect SysBusDev irqs to device specific irqs */
416
    for (i = 0; i < IIC_NIRQ; i++) {
417
        sysbus_init_irq(dev, &s->output_irq[i]);
418
    }
419

    
420
    memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
421
            "exynos4210-combiner", IIC_REGION_SIZE);
422
    sysbus_init_mmio(dev, &s->iomem);
423

    
424
    return 0;
425
}
426

    
427
static Property exynos4210_combiner_properties[] = {
428
    DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
429
    DEFINE_PROP_END_OF_LIST(),
430
};
431

    
432
static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
433
{
434
    DeviceClass *dc = DEVICE_CLASS(klass);
435
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
436

    
437
    k->init = exynos4210_combiner_init;
438
    dc->reset = exynos4210_combiner_reset;
439
    dc->props = exynos4210_combiner_properties;
440
    dc->vmsd = &vmstate_exynos4210_combiner;
441
}
442

    
443
static const TypeInfo exynos4210_combiner_info = {
444
    .name          = "exynos4210.combiner",
445
    .parent        = TYPE_SYS_BUS_DEVICE,
446
    .instance_size = sizeof(Exynos4210CombinerState),
447
    .class_init    = exynos4210_combiner_class_init,
448
};
449

    
450
static void exynos4210_combiner_register_types(void)
451
{
452
    type_register_static(&exynos4210_combiner_info);
453
}
454

    
455
type_init(exynos4210_combiner_register_types)