qemu/hw/intc/exynos4210_gic.c
<<
>>
Prefs
   1/*
   2 * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
   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#include "qemu/osdep.h"
  24#include "hw/sysbus.h"
  25#include "qemu-common.h"
  26#include "hw/irq.h"
  27#include "hw/arm/exynos4210.h"
  28
  29enum ExtGicId {
  30    EXT_GIC_ID_MDMA_LCD0 = 66,
  31    EXT_GIC_ID_PDMA0,
  32    EXT_GIC_ID_PDMA1,
  33    EXT_GIC_ID_TIMER0,
  34    EXT_GIC_ID_TIMER1,
  35    EXT_GIC_ID_TIMER2,
  36    EXT_GIC_ID_TIMER3,
  37    EXT_GIC_ID_TIMER4,
  38    EXT_GIC_ID_MCT_L0,
  39    EXT_GIC_ID_WDT,
  40    EXT_GIC_ID_RTC_ALARM,
  41    EXT_GIC_ID_RTC_TIC,
  42    EXT_GIC_ID_GPIO_XB,
  43    EXT_GIC_ID_GPIO_XA,
  44    EXT_GIC_ID_MCT_L1,
  45    EXT_GIC_ID_IEM_APC,
  46    EXT_GIC_ID_IEM_IEC,
  47    EXT_GIC_ID_NFC,
  48    EXT_GIC_ID_UART0,
  49    EXT_GIC_ID_UART1,
  50    EXT_GIC_ID_UART2,
  51    EXT_GIC_ID_UART3,
  52    EXT_GIC_ID_UART4,
  53    EXT_GIC_ID_MCT_G0,
  54    EXT_GIC_ID_I2C0,
  55    EXT_GIC_ID_I2C1,
  56    EXT_GIC_ID_I2C2,
  57    EXT_GIC_ID_I2C3,
  58    EXT_GIC_ID_I2C4,
  59    EXT_GIC_ID_I2C5,
  60    EXT_GIC_ID_I2C6,
  61    EXT_GIC_ID_I2C7,
  62    EXT_GIC_ID_SPI0,
  63    EXT_GIC_ID_SPI1,
  64    EXT_GIC_ID_SPI2,
  65    EXT_GIC_ID_MCT_G1,
  66    EXT_GIC_ID_USB_HOST,
  67    EXT_GIC_ID_USB_DEVICE,
  68    EXT_GIC_ID_MODEMIF,
  69    EXT_GIC_ID_HSMMC0,
  70    EXT_GIC_ID_HSMMC1,
  71    EXT_GIC_ID_HSMMC2,
  72    EXT_GIC_ID_HSMMC3,
  73    EXT_GIC_ID_SDMMC,
  74    EXT_GIC_ID_MIPI_CSI_4LANE,
  75    EXT_GIC_ID_MIPI_DSI_4LANE,
  76    EXT_GIC_ID_MIPI_CSI_2LANE,
  77    EXT_GIC_ID_MIPI_DSI_2LANE,
  78    EXT_GIC_ID_ONENAND_AUDI,
  79    EXT_GIC_ID_ROTATOR,
  80    EXT_GIC_ID_FIMC0,
  81    EXT_GIC_ID_FIMC1,
  82    EXT_GIC_ID_FIMC2,
  83    EXT_GIC_ID_FIMC3,
  84    EXT_GIC_ID_JPEG,
  85    EXT_GIC_ID_2D,
  86    EXT_GIC_ID_PCIe,
  87    EXT_GIC_ID_MIXER,
  88    EXT_GIC_ID_HDMI,
  89    EXT_GIC_ID_HDMI_I2C,
  90    EXT_GIC_ID_MFC,
  91    EXT_GIC_ID_TVENC,
  92};
  93
  94enum ExtInt {
  95    EXT_GIC_ID_EXTINT0 = 48,
  96    EXT_GIC_ID_EXTINT1,
  97    EXT_GIC_ID_EXTINT2,
  98    EXT_GIC_ID_EXTINT3,
  99    EXT_GIC_ID_EXTINT4,
 100    EXT_GIC_ID_EXTINT5,
 101    EXT_GIC_ID_EXTINT6,
 102    EXT_GIC_ID_EXTINT7,
 103    EXT_GIC_ID_EXTINT8,
 104    EXT_GIC_ID_EXTINT9,
 105    EXT_GIC_ID_EXTINT10,
 106    EXT_GIC_ID_EXTINT11,
 107    EXT_GIC_ID_EXTINT12,
 108    EXT_GIC_ID_EXTINT13,
 109    EXT_GIC_ID_EXTINT14,
 110    EXT_GIC_ID_EXTINT15
 111};
 112
 113/*
 114 * External GIC sources which are not from External Interrupt Combiner or
 115 * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
 116 * which is INTG16 in Internal Interrupt Combiner.
 117 */
 118
 119static const uint32_t
 120combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
 121    /* int combiner groups 16-19 */
 122    { }, { }, { }, { },
 123    /* int combiner group 20 */
 124    { 0, EXT_GIC_ID_MDMA_LCD0 },
 125    /* int combiner group 21 */
 126    { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
 127    /* int combiner group 22 */
 128    { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
 129            EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
 130    /* int combiner group 23 */
 131    { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
 132    /* int combiner group 24 */
 133    { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
 134    /* int combiner group 25 */
 135    { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
 136    /* int combiner group 26 */
 137    { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
 138            EXT_GIC_ID_UART4 },
 139    /* int combiner group 27 */
 140    { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
 141            EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
 142            EXT_GIC_ID_I2C7 },
 143    /* int combiner group 28 */
 144    { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST},
 145    /* int combiner group 29 */
 146    { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
 147     EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
 148    /* int combiner group 30 */
 149    { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
 150    /* int combiner group 31 */
 151    { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
 152    /* int combiner group 32 */
 153    { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
 154    /* int combiner group 33 */
 155    { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
 156    /* int combiner group 34 */
 157    { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
 158    /* int combiner group 35 */
 159    { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
 160    /* int combiner group 36 */
 161    { EXT_GIC_ID_MIXER },
 162    /* int combiner group 37 */
 163    { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
 164     EXT_GIC_ID_EXTINT7 },
 165    /* groups 38-50 */
 166    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
 167    /* int combiner group 51 */
 168    { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
 169    /* group 52 */
 170    { },
 171    /* int combiner group 53 */
 172    { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
 173    /* groups 54-63 */
 174    { }, { }, { }, { }, { }, { }, { }, { }, { }, { }
 175};
 176
 177#define EXYNOS4210_GIC_NIRQ 160
 178
 179#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE     0x10000
 180#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE    0x10000
 181
 182#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET      0x8000
 183#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
 184    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
 185#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
 186    ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
 187
 188#define EXYNOS4210_GIC_CPU_REGION_SIZE  0x100
 189#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
 190
 191static void exynos4210_irq_handler(void *opaque, int irq, int level)
 192{
 193    Exynos4210Irq *s = (Exynos4210Irq *)opaque;
 194
 195    /* Bypass */
 196    qemu_set_irq(s->board_irqs[irq], level);
 197}
 198
 199/*
 200 * Initialize exynos4210 IRQ subsystem stub.
 201 */
 202qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
 203{
 204    return qemu_allocate_irqs(exynos4210_irq_handler, s,
 205            EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
 206}
 207
 208/*
 209 * Initialize board IRQs.
 210 * These IRQs contain splitted Int/External Combiner and External Gic IRQs.
 211 */
 212void exynos4210_init_board_irqs(Exynos4210Irq *s)
 213{
 214    uint32_t grp, bit, irq_id, n;
 215
 216    for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
 217        irq_id = 0;
 218        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
 219                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
 220            /* MCT_G0 is passed to External GIC */
 221            irq_id = EXT_GIC_ID_MCT_G0;
 222        }
 223        if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
 224                n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
 225            /* MCT_G1 is passed to External and GIC */
 226            irq_id = EXT_GIC_ID_MCT_G1;
 227        }
 228        if (irq_id) {
 229            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
 230                    s->ext_gic_irq[irq_id-32]);
 231        } else {
 232            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
 233                    s->ext_combiner_irq[n]);
 234        }
 235    }
 236    for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
 237        /* these IDs are passed to Internal Combiner and External GIC */
 238        grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
 239        bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
 240        irq_id = combiner_grp_to_gic_id[grp -
 241                     EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
 242
 243        if (irq_id) {
 244            s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
 245                    s->ext_gic_irq[irq_id-32]);
 246        }
 247    }
 248}
 249
 250/*
 251 * Get IRQ number from exynos4210 IRQ subsystem stub.
 252 * To identify IRQ source use internal combiner group and bit number
 253 *  grp - group number
 254 *  bit - bit number inside group
 255 */
 256uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
 257{
 258    return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
 259}
 260
 261/********* GIC part *********/
 262
 263#define TYPE_EXYNOS4210_GIC "exynos4210.gic"
 264#define EXYNOS4210_GIC(obj) \
 265    OBJECT_CHECK(Exynos4210GicState, (obj), TYPE_EXYNOS4210_GIC)
 266
 267typedef struct {
 268    SysBusDevice parent_obj;
 269
 270    MemoryRegion cpu_container;
 271    MemoryRegion dist_container;
 272    MemoryRegion cpu_alias[EXYNOS4210_NCPUS];
 273    MemoryRegion dist_alias[EXYNOS4210_NCPUS];
 274    uint32_t num_cpu;
 275    DeviceState *gic;
 276} Exynos4210GicState;
 277
 278static void exynos4210_gic_set_irq(void *opaque, int irq, int level)
 279{
 280    Exynos4210GicState *s = (Exynos4210GicState *)opaque;
 281    qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
 282}
 283
 284static void exynos4210_gic_init(Object *obj)
 285{
 286    DeviceState *dev = DEVICE(obj);
 287    Exynos4210GicState *s = EXYNOS4210_GIC(obj);
 288    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 289    const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
 290    const char dist_prefix[] = "exynos4210-gic-alias_dist";
 291    char cpu_alias_name[sizeof(cpu_prefix) + 3];
 292    char dist_alias_name[sizeof(cpu_prefix) + 3];
 293    SysBusDevice *gicbusdev;
 294    uint32_t i;
 295
 296    s->gic = qdev_create(NULL, "arm_gic");
 297    qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
 298    qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
 299    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            EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
 313
 314    for (i = 0; i < s->num_cpu; i++) {
 315        /* Map CPU interface per SMP Core */
 316        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                                 0,
 321                                 EXYNOS4210_GIC_CPU_REGION_SIZE);
 322        memory_region_add_subregion(&s->cpu_container,
 323                EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
 324
 325        /* Map Distributor per SMP Core */
 326        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                                 0,
 331                                 EXYNOS4210_GIC_DIST_REGION_SIZE);
 332        memory_region_add_subregion(&s->dist_container,
 333                EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
 334    }
 335
 336    sysbus_init_mmio(sbd, &s->cpu_container);
 337    sysbus_init_mmio(sbd, &s->dist_container);
 338}
 339
 340static Property exynos4210_gic_properties[] = {
 341    DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
 342    DEFINE_PROP_END_OF_LIST(),
 343};
 344
 345static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
 346{
 347    DeviceClass *dc = DEVICE_CLASS(klass);
 348
 349    dc->props = exynos4210_gic_properties;
 350}
 351
 352static const TypeInfo exynos4210_gic_info = {
 353    .name          = TYPE_EXYNOS4210_GIC,
 354    .parent        = TYPE_SYS_BUS_DEVICE,
 355    .instance_size = sizeof(Exynos4210GicState),
 356    .instance_init = exynos4210_gic_init,
 357    .class_init    = exynos4210_gic_class_init,
 358};
 359
 360static void exynos4210_gic_register_types(void)
 361{
 362    type_register_static(&exynos4210_gic_info);
 363}
 364
 365type_init(exynos4210_gic_register_types)
 366
 367/* IRQ OR Gate struct.
 368 *
 369 * This device models an OR gate. There are n_in input qdev gpio lines and one
 370 * output sysbus IRQ line. The output IRQ level is formed as OR between all
 371 * gpio inputs.
 372 */
 373
 374#define TYPE_EXYNOS4210_IRQ_GATE "exynos4210.irq_gate"
 375#define EXYNOS4210_IRQ_GATE(obj) \
 376    OBJECT_CHECK(Exynos4210IRQGateState, (obj), TYPE_EXYNOS4210_IRQ_GATE)
 377
 378typedef struct Exynos4210IRQGateState {
 379    SysBusDevice parent_obj;
 380
 381    uint32_t n_in;      /* inputs amount */
 382    uint32_t *level;    /* input levels */
 383    qemu_irq out;       /* output IRQ */
 384} Exynos4210IRQGateState;
 385
 386static Property exynos4210_irq_gate_properties[] = {
 387    DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1),
 388    DEFINE_PROP_END_OF_LIST(),
 389};
 390
 391static const VMStateDescription vmstate_exynos4210_irq_gate = {
 392    .name = "exynos4210.irq_gate",
 393    .version_id = 2,
 394    .minimum_version_id = 2,
 395    .fields = (VMStateField[]) {
 396        VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, n_in),
 397        VMSTATE_END_OF_LIST()
 398    }
 399};
 400
 401/* Process a change in IRQ input. */
 402static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
 403{
 404    Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque;
 405    uint32_t i;
 406
 407    assert(irq < s->n_in);
 408
 409    s->level[irq] = level;
 410
 411    for (i = 0; i < s->n_in; i++) {
 412        if (s->level[i] >= 1) {
 413            qemu_irq_raise(s->out);
 414            return;
 415        }
 416    }
 417
 418    qemu_irq_lower(s->out);
 419}
 420
 421static void exynos4210_irq_gate_reset(DeviceState *d)
 422{
 423    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(d);
 424
 425    memset(s->level, 0, s->n_in * sizeof(*s->level));
 426}
 427
 428/*
 429 * IRQ Gate initialization.
 430 */
 431static void exynos4210_irq_gate_init(Object *obj)
 432{
 433    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(obj);
 434    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 435
 436    sysbus_init_irq(sbd, &s->out);
 437}
 438
 439static void exynos4210_irq_gate_realize(DeviceState *dev, Error **errp)
 440{
 441    Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev);
 442
 443    /* Allocate general purpose input signals and connect a handler to each of
 444     * 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
 450static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
 451{
 452    DeviceClass *dc = DEVICE_CLASS(klass);
 453
 454    dc->reset = exynos4210_irq_gate_reset;
 455    dc->vmsd = &vmstate_exynos4210_irq_gate;
 456    dc->props = exynos4210_irq_gate_properties;
 457    dc->realize = exynos4210_irq_gate_realize;
 458}
 459
 460static const TypeInfo exynos4210_irq_gate_info = {
 461    .name          = TYPE_EXYNOS4210_IRQ_GATE,
 462    .parent        = TYPE_SYS_BUS_DEVICE,
 463    .instance_size = sizeof(Exynos4210IRQGateState),
 464    .instance_init = exynos4210_irq_gate_init,
 465    .class_init    = exynos4210_irq_gate_class_init,
 466};
 467
 468static void exynos4210_irq_gate_register_types(void)
 469{
 470    type_register_static(&exynos4210_irq_gate_info);
 471}
 472
 473type_init(exynos4210_irq_gate_register_types)
 474