Statistics
| Branch: | Revision:

root / hw / exynos4210_combiner.c @ 37952117

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 "sysbus.h"
31

    
32
#include "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, target_phys_addr_t 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, target_phys_addr_t 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
    return;
352
}
353

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

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

    
368
    group_n = get_combiner_group_and_bit(irq, &bit_n);
369

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

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

    
382
    exynos4210_combiner_update(s, group_n);
383

    
384
    return;
385
}
386

    
387
static void exynos4210_combiner_reset(DeviceState *d)
388
{
389
    struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
390

    
391
    memset(&s->group, 0, sizeof(s->group));
392
    memset(&s->reg_set, 0, sizeof(s->reg_set));
393

    
394
    s->reg_set[0xC0 >> 2] = 0x01010101;
395
    s->reg_set[0xC4 >> 2] = 0x01010101;
396
    s->reg_set[0xD0 >> 2] = 0x01010101;
397
    s->reg_set[0xD4 >> 2] = 0x01010101;
398
}
399

    
400
static const MemoryRegionOps exynos4210_combiner_ops = {
401
    .read = exynos4210_combiner_read,
402
    .write = exynos4210_combiner_write,
403
    .endianness = DEVICE_NATIVE_ENDIAN,
404
};
405

    
406
/*
407
 * Internal Combiner initialization.
408
 */
409
static int exynos4210_combiner_init(SysBusDevice *dev)
410
{
411
    unsigned int i;
412
    struct Exynos4210CombinerState *s =
413
            FROM_SYSBUS(struct Exynos4210CombinerState, dev);
414

    
415
    /* Allocate general purpose input signals and connect a handler to each of
416
     * them */
417
    qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
418

    
419
    /* Connect SysBusDev irqs to device specific irqs */
420
    for (i = 0; i < IIC_NIRQ; i++) {
421
        sysbus_init_irq(dev, &s->output_irq[i]);
422
    }
423

    
424
    memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
425
            "exynos4210-combiner", IIC_REGION_SIZE);
426
    sysbus_init_mmio(dev, &s->iomem);
427

    
428
    return 0;
429
}
430

    
431
static Property exynos4210_combiner_properties[] = {
432
    DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
433
    DEFINE_PROP_END_OF_LIST(),
434
};
435

    
436
static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
437
{
438
    DeviceClass *dc = DEVICE_CLASS(klass);
439
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
440

    
441
    k->init = exynos4210_combiner_init;
442
    dc->reset = exynos4210_combiner_reset;
443
    dc->props = exynos4210_combiner_properties;
444
    dc->vmsd = &vmstate_exynos4210_combiner;
445
}
446

    
447
static TypeInfo exynos4210_combiner_info = {
448
    .name          = "exynos4210.combiner",
449
    .parent        = TYPE_SYS_BUS_DEVICE,
450
    .instance_size = sizeof(Exynos4210CombinerState),
451
    .class_init    = exynos4210_combiner_class_init,
452
};
453

    
454
static void exynos4210_combiner_register_types(void)
455
{
456
    type_register_static(&exynos4210_combiner_info);
457
}
458

    
459
type_init(exynos4210_combiner_register_types)