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