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

P
Peter Maydell 已提交
23
#include "qemu/osdep.h"
24
#include "hw/sysbus.h"
25
#include "qemu-common.h"
26
#include "hw/irq.h"
P
Paolo Bonzini 已提交
27
#include "hw/arm/exynos4210.h"
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

enum ExtGicId {
    EXT_GIC_ID_MDMA_LCD0 = 66,
    EXT_GIC_ID_PDMA0,
    EXT_GIC_ID_PDMA1,
    EXT_GIC_ID_TIMER0,
    EXT_GIC_ID_TIMER1,
    EXT_GIC_ID_TIMER2,
    EXT_GIC_ID_TIMER3,
    EXT_GIC_ID_TIMER4,
    EXT_GIC_ID_MCT_L0,
    EXT_GIC_ID_WDT,
    EXT_GIC_ID_RTC_ALARM,
    EXT_GIC_ID_RTC_TIC,
    EXT_GIC_ID_GPIO_XB,
    EXT_GIC_ID_GPIO_XA,
    EXT_GIC_ID_MCT_L1,
    EXT_GIC_ID_IEM_APC,
    EXT_GIC_ID_IEM_IEC,
    EXT_GIC_ID_NFC,
    EXT_GIC_ID_UART0,
    EXT_GIC_ID_UART1,
    EXT_GIC_ID_UART2,
    EXT_GIC_ID_UART3,
    EXT_GIC_ID_UART4,
    EXT_GIC_ID_MCT_G0,
    EXT_GIC_ID_I2C0,
    EXT_GIC_ID_I2C1,
    EXT_GIC_ID_I2C2,
    EXT_GIC_ID_I2C3,
    EXT_GIC_ID_I2C4,
    EXT_GIC_ID_I2C5,
    EXT_GIC_ID_I2C6,
    EXT_GIC_ID_I2C7,
    EXT_GIC_ID_SPI0,
    EXT_GIC_ID_SPI1,
    EXT_GIC_ID_SPI2,
    EXT_GIC_ID_MCT_G1,
    EXT_GIC_ID_USB_HOST,
    EXT_GIC_ID_USB_DEVICE,
    EXT_GIC_ID_MODEMIF,
    EXT_GIC_ID_HSMMC0,
    EXT_GIC_ID_HSMMC1,
    EXT_GIC_ID_HSMMC2,
    EXT_GIC_ID_HSMMC3,
    EXT_GIC_ID_SDMMC,
    EXT_GIC_ID_MIPI_CSI_4LANE,
    EXT_GIC_ID_MIPI_DSI_4LANE,
    EXT_GIC_ID_MIPI_CSI_2LANE,
    EXT_GIC_ID_MIPI_DSI_2LANE,
    EXT_GIC_ID_ONENAND_AUDI,
    EXT_GIC_ID_ROTATOR,
    EXT_GIC_ID_FIMC0,
    EXT_GIC_ID_FIMC1,
    EXT_GIC_ID_FIMC2,
    EXT_GIC_ID_FIMC3,
    EXT_GIC_ID_JPEG,
    EXT_GIC_ID_2D,
    EXT_GIC_ID_PCIe,
    EXT_GIC_ID_MIXER,
    EXT_GIC_ID_HDMI,
    EXT_GIC_ID_HDMI_I2C,
    EXT_GIC_ID_MFC,
    EXT_GIC_ID_TVENC,
};

enum ExtInt {
    EXT_GIC_ID_EXTINT0 = 48,
    EXT_GIC_ID_EXTINT1,
    EXT_GIC_ID_EXTINT2,
    EXT_GIC_ID_EXTINT3,
    EXT_GIC_ID_EXTINT4,
    EXT_GIC_ID_EXTINT5,
    EXT_GIC_ID_EXTINT6,
    EXT_GIC_ID_EXTINT7,
    EXT_GIC_ID_EXTINT8,
    EXT_GIC_ID_EXTINT9,
    EXT_GIC_ID_EXTINT10,
    EXT_GIC_ID_EXTINT11,
    EXT_GIC_ID_EXTINT12,
    EXT_GIC_ID_EXTINT13,
    EXT_GIC_ID_EXTINT14,
    EXT_GIC_ID_EXTINT15
};

/*
 * External GIC sources which are not from External Interrupt Combiner or
 * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
 * which is INTG16 in Internal Interrupt Combiner.
 */

static uint32_t
combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
    /* int combiner groups 16-19 */
    { }, { }, { }, { },
    /* int combiner group 20 */
    { 0, EXT_GIC_ID_MDMA_LCD0 },
    /* int combiner group 21 */
    { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
    /* int combiner group 22 */
    { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
            EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
    /* int combiner group 23 */
    { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
    /* int combiner group 24 */
    { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
    /* int combiner group 25 */
    { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
    /* int combiner group 26 */
    { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
            EXT_GIC_ID_UART4 },
    /* int combiner group 27 */
    { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
            EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
            EXT_GIC_ID_I2C7 },
    /* int combiner group 28 */
L
Liming Wang 已提交
144
    { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST},
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    /* int combiner group 29 */
    { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
     EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
    /* int combiner group 30 */
    { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
    /* int combiner group 31 */
    { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
    /* int combiner group 32 */
    { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
    /* int combiner group 33 */
    { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
    /* int combiner group 34 */
    { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
    /* int combiner group 35 */
    { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
    /* int combiner group 36 */
    { EXT_GIC_ID_MIXER },
    /* int combiner group 37 */
    { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
     EXT_GIC_ID_EXTINT7 },
    /* groups 38-50 */
    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
    /* int combiner group 51 */
    { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
    /* group 52 */
    { },
    /* int combiner group 53 */
    { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
    /* groups 54-63 */
    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }
};

#define EXYNOS4210_GIC_NIRQ 160

#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE     0x10000
#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE    0x10000

#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET      0x8000
#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)

#define EXYNOS4210_GIC_CPU_REGION_SIZE  0x100
#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000

static void exynos4210_irq_handler(void *opaque, int irq, int level)
{
    Exynos4210Irq *s = (Exynos4210Irq *)opaque;

    /* Bypass */
    qemu_set_irq(s->board_irqs[irq], level);
}

/*
 * Initialize exynos4210 IRQ subsystem stub.
 */
qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
{
    return qemu_allocate_irqs(exynos4210_irq_handler, s,
            EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
}

/*
 * Initialize board IRQs.
 * These IRQs contain splitted Int/External Combiner and External Gic IRQs.
 */
void exynos4210_init_board_irqs(Exynos4210Irq *s)
{
    uint32_t grp, bit, irq_id, n;

    for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
        irq_id = 0;
        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
            /* MCT_G0 is passed to External GIC */
            irq_id = EXT_GIC_ID_MCT_G0;
        }
        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
            /* MCT_G1 is passed to External and GIC */
            irq_id = EXT_GIC_ID_MCT_G1;
        }
        if (irq_id) {
            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
                    s->ext_gic_irq[irq_id-32]);
231 232 233
        } else {
            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
                    s->ext_combiner_irq[n]);
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
        }
    }
    for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
        /* these IDs are passed to Internal Combiner and External GIC */
        grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
        irq_id = combiner_grp_to_gic_id[grp -
                     EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];

        if (irq_id) {
            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
                    s->ext_gic_irq[irq_id-32]);
        }
    }
}

/*
 * Get IRQ number from exynos4210 IRQ subsystem stub.
 * To identify IRQ source use internal combiner group and bit number
 *  grp - group number
 *  bit - bit number inside group
 */
uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
{
    return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
}

/********* GIC part *********/

263 264 265 266
#define TYPE_EXYNOS4210_GIC "exynos4210.gic"
#define EXYNOS4210_GIC(obj) \
    OBJECT_CHECK(Exynos4210GicState, (obj), TYPE_EXYNOS4210_GIC)

267
typedef struct {
268 269
    SysBusDevice parent_obj;

270 271
    MemoryRegion cpu_container;
    MemoryRegion dist_container;
272 273
    MemoryRegion cpu_alias[EXYNOS4210_NCPUS];
    MemoryRegion dist_alias[EXYNOS4210_NCPUS];
274
    uint32_t num_cpu;
275
    DeviceState *gic;
276 277
} Exynos4210GicState;

278 279 280 281 282 283
static void exynos4210_gic_set_irq(void *opaque, int irq, int level)
{
    Exynos4210GicState *s = (Exynos4210GicState *)opaque;
    qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
}

284
static void exynos4210_gic_init(Object *obj)
285
{
286 287 288
    DeviceState *dev = DEVICE(obj);
    Exynos4210GicState *s = EXYNOS4210_GIC(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
289 290 291 292
    const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
    const char dist_prefix[] = "exynos4210-gic-alias_dist";
    char cpu_alias_name[sizeof(cpu_prefix) + 3];
    char dist_alias_name[sizeof(cpu_prefix) + 3];
293 294
    SysBusDevice *gicbusdev;
    uint32_t i;
295 296 297 298 299

    s->gic = qdev_create(NULL, "arm_gic");
    qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
    qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
    qdev_init_nofail(s->gic);
300
    gicbusdev = SYS_BUS_DEVICE(s->gic);
301 302

    /* Pass through outbound IRQ lines from the GIC */
303
    sysbus_pass_irq(sbd, gicbusdev);
304

305
    /* Pass through inbound GPIO lines to the GIC */
306
    qdev_init_gpio_in(dev, exynos4210_gic_set_irq,
307
                      EXYNOS4210_GIC_NIRQ - 32);
308

309
    memory_region_init(&s->cpu_container, obj, "exynos4210-cpu-container",
310
            EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
311
    memory_region_init(&s->dist_container, obj, "exynos4210-dist-container",
312 313 314 315 316
            EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);

    for (i = 0; i < s->num_cpu; i++) {
        /* Map CPU interface per SMP Core */
        sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
317
        memory_region_init_alias(&s->cpu_alias[i], obj,
318
                                 cpu_alias_name,
319
                                 sysbus_mmio_get_region(gicbusdev, 1),
320 321 322 323 324 325 326
                                 0,
                                 EXYNOS4210_GIC_CPU_REGION_SIZE);
        memory_region_add_subregion(&s->cpu_container,
                EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);

        /* Map Distributor per SMP Core */
        sprintf(dist_alias_name, "%s%x", dist_prefix, i);
327
        memory_region_init_alias(&s->dist_alias[i], obj,
328
                                 dist_alias_name,
329
                                 sysbus_mmio_get_region(gicbusdev, 0),
330 331 332 333 334 335
                                 0,
                                 EXYNOS4210_GIC_DIST_REGION_SIZE);
        memory_region_add_subregion(&s->dist_container,
                EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
    }

336 337
    sysbus_init_mmio(sbd, &s->cpu_container);
    sysbus_init_mmio(sbd, &s->dist_container);
338 339 340 341 342 343 344 345 346 347 348 349 350 351
}

static Property exynos4210_gic_properties[] = {
    DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
    DEFINE_PROP_END_OF_LIST(),
};

static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->props = exynos4210_gic_properties;
}

352
static const TypeInfo exynos4210_gic_info = {
353
    .name          = TYPE_EXYNOS4210_GIC,
354 355
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(Exynos4210GicState),
356
    .instance_init = exynos4210_gic_init,
357 358 359 360 361 362 363 364 365 366
    .class_init    = exynos4210_gic_class_init,
};

static void exynos4210_gic_register_types(void)
{
    type_register_static(&exynos4210_gic_info);
}

type_init(exynos4210_gic_register_types)

367 368 369 370 371
/* IRQ OR Gate struct.
 *
 * This device models an OR gate. There are n_in input qdev gpio lines and one
 * output sysbus IRQ line. The output IRQ level is formed as OR between all
 * gpio inputs.
372
 */
373 374 375 376 377 378 379

#define TYPE_EXYNOS4210_IRQ_GATE "exynos4210.irq_gate"
#define EXYNOS4210_IRQ_GATE(obj) \
    OBJECT_CHECK(Exynos4210IRQGateState, (obj), TYPE_EXYNOS4210_IRQ_GATE)

typedef struct Exynos4210IRQGateState {
    SysBusDevice parent_obj;
380

381 382 383
    uint32_t n_in;      /* inputs amount */
    uint32_t *level;    /* input levels */
    qemu_irq out;       /* output IRQ */
384 385
} Exynos4210IRQGateState;

386 387 388 389 390
static Property exynos4210_irq_gate_properties[] = {
    DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1),
    DEFINE_PROP_END_OF_LIST(),
};

391 392
static const VMStateDescription vmstate_exynos4210_irq_gate = {
    .name = "exynos4210.irq_gate",
393 394
    .version_id = 2,
    .minimum_version_id = 2,
395
    .fields = (VMStateField[]) {
396
        VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, n_in),
397 398 399 400
        VMSTATE_END_OF_LIST()
    }
};

401
/* Process a change in IRQ input. */
402 403
static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
{
404 405
    Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque;
    uint32_t i;
406

407
    assert(irq < s->n_in);
408

409 410 411 412 413 414 415
    s->level[irq] = level;

    for (i = 0; i < s->n_in; i++) {
        if (s->level[i] >= 1) {
            qemu_irq_raise(s->out);
            return;
        }
416 417
    }

418
    qemu_irq_lower(s->out);
419 420 421 422
}

static void exynos4210_irq_gate_reset(DeviceState *d)
{
423
    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(d);
424

425
    memset(s->level, 0, s->n_in * sizeof(*s->level));
426 427 428 429 430
}

/*
 * IRQ Gate initialization.
 */
431 432 433 434 435 436 437 438 439
static void exynos4210_irq_gate_init(Object *obj)
{
    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);

    sysbus_init_irq(sbd, &s->out);
}

static void exynos4210_irq_gate_realize(DeviceState *dev, Error **errp)
440
{
441
    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev);
442 443 444

    /* Allocate general purpose input signals and connect a handler to each of
     * them */
445
    qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in);
446

447
    s->level = g_malloc0(s->n_in * sizeof(*s->level));
448 449 450 451 452 453 454 455
}

static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->reset = exynos4210_irq_gate_reset;
    dc->vmsd = &vmstate_exynos4210_irq_gate;
456
    dc->props = exynos4210_irq_gate_properties;
457
    dc->realize = exynos4210_irq_gate_realize;
458 459
}

460
static const TypeInfo exynos4210_irq_gate_info = {
461
    .name          = TYPE_EXYNOS4210_IRQ_GATE,
462 463
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(Exynos4210IRQGateState),
464
    .instance_init = exynos4210_irq_gate_init,
465 466 467 468 469 470 471 472 473
    .class_init    = exynos4210_irq_gate_class_init,
};

static void exynos4210_irq_gate_register_types(void)
{
    type_register_static(&exynos4210_irq_gate_info);
}

type_init(exynos4210_irq_gate_register_types)