qemu/hw/intc/xilinx-zynqmp-gicp.c
<<
>>
Prefs
   1/*
   2 * This file contains implementation of GIC Proxy component.
   3 *
   4 * 2014 Aggios, Inc.
   5 *
   6 * Written by Strahinja Jankovic <strahinja.jankovic@aggios.com>
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "qapi/error.h"
  29#include "hw/sysbus.h"
  30#include "hw/register.h"
  31#include "qemu/bitops.h"
  32#include "qemu/log.h"
  33
  34#include "hw/fdt_generic_util.h"
  35
  36#ifndef GIC_PROXY_ERR_DEBUG
  37#define GIC_PROXY_ERR_DEBUG 0
  38#endif
  39
  40#define TYPE_XILINX_GIC_PROXY "xlnx.zynqmp-gicp"
  41
  42#define XILINX_GIC_PROXY(obj) \
  43     OBJECT_CHECK(GICProxy, (obj), TYPE_XILINX_GIC_PROXY)
  44
  45#define MAX_INTS            160
  46#define GICP_GROUPS         5
  47#define GICP_GROUP_STRIDE   0x14
  48
  49REG32(GICP0_IRQ_STATUS, 0x0)
  50REG32(GICP0_IRQ_MASK, 0x4)
  51REG32(GICP0_IRQ_ENABLE, 0x8)
  52REG32(GICP0_IRQ_DISABLE, 0xc)
  53REG32(GICP0_IRQ_TRIGGER, 0x10)
  54    #define R_GICP0_RSVD    0x000000ff
  55    #define R_GICP1_RSVD    0
  56    #define R_GICP2_RSVD    0
  57    #define R_GICP3_RSVD    0x000000ff
  58    #define R_GICP4_RSVD    0xf0000000
  59REG32(GICP_PMU_IRQ_STATUS, 0xa0)
  60REG32(GICP_PMU_IRQ_MASK, 0xa4)
  61REG32(GICP_PMU_IRQ_ENABLE, 0xa8)
  62REG32(GICP_PMU_IRQ_DISABLE, 0xac)
  63REG32(GICP_PMU_IRQ_TRIGGER, 0xb0)
  64
  65#define R_MAX (R_GICP_PMU_IRQ_TRIGGER + 1)
  66
  67typedef struct GICProxy {
  68    SysBusDevice parent_obj;
  69    MemoryRegion iomem;
  70    qemu_irq irq;
  71
  72    uint32_t regs[R_MAX];
  73    RegisterInfo regs_info[R_MAX];
  74} GICProxy;
  75
  76/* Mask and status registers are needed for checking if interrupt
  77 * needs to be triggered.
  78 */
  79#define GICPN_STATUS_REG(n) ((A_GICP0_IRQ_STATUS + \
  80                             (n) * GICP_GROUP_STRIDE) >> 2)
  81#define GICPN_MASK_REG(n)   ((A_GICP0_IRQ_MASK + \
  82                             (n) * GICP_GROUP_STRIDE) >> 2)
  83
  84static void gicp_update_irq(void *opaque)
  85{
  86    GICProxy *s = XILINX_GIC_PROXY(opaque);
  87    bool pending = s->regs[R_GICP_PMU_IRQ_STATUS] &
  88                   ~s->regs[R_GICP_PMU_IRQ_MASK];
  89
  90    qemu_set_irq(s->irq, pending);
  91}
  92
  93/* Functions for handling changes to top level interrupt registers. */
  94
  95static void gicp_update(void *opaque, uint8_t nr)
  96{
  97    GICProxy *s = XILINX_GIC_PROXY(opaque);
  98
  99    if (s->regs[GICPN_STATUS_REG(nr)] & ~s->regs[GICPN_MASK_REG(nr)]) {
 100        s->regs[R_GICP_PMU_IRQ_STATUS] |= 1 << nr;
 101    } else {
 102        s->regs[R_GICP_PMU_IRQ_STATUS] &= ~(1 << nr);
 103    }
 104    gicp_update_irq(s);
 105}
 106
 107static void gicp_status_postw(RegisterInfo *reg, uint64_t val64)
 108{
 109    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 110    unsigned int i;
 111
 112    for (i = 0; i < GICP_GROUPS; i++) {
 113        gicp_update(s, i);
 114    }
 115}
 116
 117static void gicp_enable_postw(RegisterInfo *reg, uint64_t val64)
 118{
 119    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 120    uint32_t val = val64;
 121
 122    s->regs[R_GICP_PMU_IRQ_MASK] &= ~val;
 123    gicp_update_irq(s);
 124}
 125
 126static void gicp_disable_postw(RegisterInfo *reg, uint64_t val64)
 127{
 128    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 129    uint32_t val = val64;
 130
 131    s->regs[R_GICP_PMU_IRQ_MASK] |= val;
 132    gicp_update_irq(s);
 133}
 134
 135static void gicp_trigger_postw(RegisterInfo *reg, uint64_t val64)
 136{
 137    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 138    uint32_t val = val64;
 139
 140    s->regs[R_GICP_PMU_IRQ_STATUS] |= val;
 141    gicp_update_irq(s);
 142}
 143
 144/* Functions for handling changes to each interrupt register. */
 145
 146static void gicpn_status_postw(RegisterInfo *reg, uint64_t val64)
 147{
 148    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 149    uint64_t nr = (uint64_t)reg->access->opaque;
 150
 151    gicp_update(s, nr);
 152}
 153
 154static void gicpn_enable_postw(RegisterInfo *reg, uint64_t val64)
 155{
 156    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 157    uint32_t val = val64;
 158    uint64_t nr = (uint64_t)reg->access->opaque;
 159
 160    s->regs[GICPN_MASK_REG(nr)] &= ~val;
 161    gicp_update(s, nr);
 162}
 163
 164static void gicpn_disable_postw(RegisterInfo *reg, uint64_t val64)
 165{
 166    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 167    uint32_t val = val64;
 168    uint64_t nr = (uint64_t)reg->access->opaque;
 169
 170    s->regs[GICPN_MASK_REG(nr)] |= val;
 171    gicp_update(s, nr);
 172}
 173
 174static void gicpn_trigger_postw(RegisterInfo *reg, uint64_t val64)
 175{
 176    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 177    uint32_t val = val64;
 178    uint64_t nr = (uint64_t)reg->access->opaque;
 179
 180    s->regs[GICPN_STATUS_REG(nr)] |= val;
 181    gicp_update(s, nr);
 182}
 183
 184/* Return 0 and log if reading from write-only register. */
 185static uint64_t gicp_wo_postr(RegisterInfo *reg, uint64_t val64)
 186{
 187    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 188    qemu_log_mask(LOG_GUEST_ERROR,
 189                  "%s: Reading from wo register at %" HWADDR_PRIx "\n",
 190                  object_get_canonical_path(OBJECT(s)),
 191                  reg->access->decode.addr);
 192    return 0;
 193}
 194
 195/* Read-as-zero high 24 bits. */
 196static uint64_t gicp_raz_hi24_postr(RegisterInfo *reg, uint64_t val64)
 197{
 198    return val64 & 0xff;
 199}
 200
 201static RegisterAccessInfo gic_proxy_regs_info[] = {
 202#define GICPN_REG_DEFS(n) \
 203    {   .name = "GICP" #n "_IRQ_STATUS",                            \
 204        .decode.addr = A_GICP0_IRQ_STATUS + n * GICP_GROUP_STRIDE,  \
 205        .w1c = 0xffffffff,                                          \
 206        .rsvd = R_GICP ## n ## _RSVD,                               \
 207        .post_write = gicpn_status_postw,                           \
 208        .opaque = (void *)n,                                        \
 209    },{ .name = "GICP" #n "_IRQ_MASK",                              \
 210        .decode.addr = A_GICP0_IRQ_MASK + n * GICP_GROUP_STRIDE,    \
 211        .ro = 0xffffffff,                                           \
 212        .rsvd = R_GICP ## n ## _RSVD,                               \
 213        .reset = 0xffffffff,                                        \
 214    },{ .name = "GICP" #n "_IRQ_ENABLE",                            \
 215        .decode.addr = A_GICP0_IRQ_ENABLE + n * GICP_GROUP_STRIDE,  \
 216        .rsvd = R_GICP ## n ## _RSVD,                               \
 217        .post_read = gicp_wo_postr,                                 \
 218        .post_write = gicpn_enable_postw,                           \
 219        .opaque = (void *)n,                                        \
 220    },{ .name = "GICP" #n "_IRQ_DISABLE",                           \
 221        .decode.addr = A_GICP0_IRQ_DISABLE + n * GICP_GROUP_STRIDE, \
 222        .rsvd = R_GICP ## n ## _RSVD,                               \
 223        .post_read = gicp_wo_postr,                                 \
 224        .post_write = gicpn_disable_postw,                          \
 225        .opaque = (void *)n,                                        \
 226    },{ .name = "GICP" #n "_IRQ_TRIGGER",                           \
 227        .decode.addr = A_GICP0_IRQ_TRIGGER + n * GICP_GROUP_STRIDE, \
 228        .rsvd = R_GICP ## n ## _RSVD,                               \
 229        .post_read = gicp_wo_postr,                                 \
 230        .post_write = gicpn_trigger_postw,                          \
 231        .opaque = (void *)n,                                        \
 232    }
 233    GICPN_REG_DEFS(0),
 234    GICPN_REG_DEFS(1),
 235    GICPN_REG_DEFS(2),
 236    GICPN_REG_DEFS(3),
 237    GICPN_REG_DEFS(4),
 238      { .name = "GICP_PMU_IRQ_STATUS",  .decode.addr = A_GICP_PMU_IRQ_STATUS,
 239        .w1c = 0x000000ff,
 240        .rsvd = 0xffffffe0,
 241        .post_read = gicp_raz_hi24_postr,
 242        .post_write = gicp_status_postw,
 243    },{ .name = "GICP_PMU_IRQ_MASK",  .decode.addr = A_GICP_PMU_IRQ_MASK,
 244        .ro = 0x000000ff,
 245        .rsvd = 0xffffffe0,
 246        .reset = 0x000000ff,
 247        .post_read = gicp_raz_hi24_postr,
 248    },{ .name = "GICP_PMU_IRQ_ENABLE",  .decode.addr = A_GICP_PMU_IRQ_ENABLE,
 249        .rsvd = 0xffffffe0,
 250        .post_read = gicp_wo_postr,
 251        .post_write = gicp_enable_postw,
 252    },{ .name = "GICP_PMU_IRQ_DISABLE",  .decode.addr = A_GICP_PMU_IRQ_DISABLE,
 253        .rsvd = 0xffffffe0,
 254        .post_read = gicp_wo_postr,
 255        .post_write = gicp_disable_postw,
 256    },{ .name = "GICP_PMU_IRQ_TRIGGER",  .decode.addr = A_GICP_PMU_IRQ_TRIGGER,
 257        .rsvd = 0xffffffe0,
 258        .post_read = gicp_wo_postr,
 259        .post_write = gicp_trigger_postw,
 260    }
 261};
 262
 263static void gic_proxy_reset(DeviceState *dev)
 264{
 265    GICProxy *s = XILINX_GIC_PROXY(dev);
 266    unsigned int i;
 267
 268    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 269        register_reset(&s->regs_info[i]);
 270    }
 271}
 272
 273static uint64_t gic_proxy_read(void *opaque, hwaddr addr, unsigned size)
 274{
 275    GICProxy *s = XILINX_GIC_PROXY(opaque);
 276    RegisterInfo *r = &s->regs_info[addr / 4];
 277
 278    if (!r->data) {
 279        qemu_log_mask(LOG_GUEST_ERROR,
 280                      "%s: Decode error: read from %" HWADDR_PRIx "\n",
 281                      object_get_canonical_path(OBJECT(s)),
 282                      addr);
 283        return 0;
 284    }
 285    return register_read(r);
 286}
 287
 288static void gic_proxy_write(void *opaque, hwaddr addr, uint64_t value,
 289                      unsigned size)
 290{
 291    GICProxy *s = XILINX_GIC_PROXY(opaque);
 292    RegisterInfo *r = &s->regs_info[addr / 4];
 293
 294    if (!r->data) {
 295        qemu_log_mask(LOG_GUEST_ERROR,
 296                      "%s: Decode error: write %" HWADDR_PRIx "=%" PRIx64 "\n",
 297                      object_get_canonical_path(OBJECT(s)),
 298                      addr, value);
 299        return;
 300    }
 301    register_write(r, value, ~0);
 302}
 303
 304static const MemoryRegionOps gic_proxy_ops = {
 305    .read = gic_proxy_read,
 306    .write = gic_proxy_write,
 307    .endianness = DEVICE_LITTLE_ENDIAN,
 308    .valid = {
 309        .min_access_size = 4,
 310        .max_access_size = 4,
 311    },
 312};
 313
 314static void gic_proxy_realize(DeviceState *dev, Error **errp)
 315{
 316    GICProxy *s = XILINX_GIC_PROXY(dev);
 317    const char *prefix = object_get_canonical_path(OBJECT(dev));
 318    unsigned int i;
 319
 320    for (i = 0; i < ARRAY_SIZE(gic_proxy_regs_info); ++i) {
 321        RegisterInfo *r = &s->regs_info[gic_proxy_regs_info[i].decode.addr/4];
 322
 323        *r = (RegisterInfo) {
 324            .data = (uint8_t *)&s->regs[
 325                    gic_proxy_regs_info[i].decode.addr/4],
 326            .data_size = sizeof(uint32_t),
 327            .access = &gic_proxy_regs_info[i],
 328            .debug = GIC_PROXY_ERR_DEBUG,
 329            .prefix = prefix,
 330            .opaque = s,
 331        };
 332        register_init(r);
 333    }
 334}
 335
 336static void gic_proxy_set_irq(void *opaque, int irq, int level)
 337{
 338    GICProxy *s = XILINX_GIC_PROXY(opaque);
 339    int group = irq / 32;
 340    int bit = irq % 32;
 341
 342    if (level) {
 343        s->regs[GICPN_STATUS_REG(group)] |= 1 << bit;
 344    } else {
 345        s->regs[GICPN_STATUS_REG(group)] &= ~(1 << bit);
 346    }
 347    gicp_update(s, group);
 348}
 349
 350static void gic_proxy_init(Object *obj)
 351{
 352    GICProxy *s = XILINX_GIC_PROXY(obj);
 353    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 354    DeviceState *dev = DEVICE(s);
 355
 356    /* IRQ grouping:
 357     * [0..31] - GICP0
 358     * [32..63] - GICP1
 359     * [64..95] - GICP2
 360     * [96..127] - GICP3
 361     * [128..159] - GICP4
 362     */
 363    qdev_init_gpio_in(dev, gic_proxy_set_irq, MAX_INTS);
 364    qdev_init_gpio_out_named(dev, &s->irq, "gicp-irq", 1);
 365
 366    memory_region_init_io(&s->iomem, obj, &gic_proxy_ops, s,
 367                          TYPE_XILINX_GIC_PROXY, R_MAX * 4);
 368    sysbus_init_mmio(sbd, &s->iomem);
 369    sysbus_init_irq(sbd, &s->irq);
 370}
 371
 372static int gic_proxy_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
 373                          uint32_t *cells, int ncells, int max,
 374                          Error **errp)
 375{
 376    int idx;
 377
 378    if (ncells != 3) {
 379        error_setg(errp, "Xilinx GIC Proxy requires 3 interrupt cells, "
 380                   "%d cells given", ncells);
 381        return 0;
 382    }
 383    idx = cells[1];
 384
 385    switch (cells[0]) {
 386    case 0:
 387        if (idx >= MAX_INTS) {
 388            error_setg(errp, "Xilinx GIC Proxy has maximum index of %" PRId32
 389                       ", index %" PRId32 " given", MAX_INTS - 1, idx);
 390            return 0;
 391        }
 392        (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[1]);
 393        return 1;
 394    default:
 395        error_setg(errp, "Invalid cell 0 value in interrupt binding: %d",
 396                   cells[0]);
 397        return 0;
 398    }
 399};
 400
 401static const VMStateDescription vmstate_gic_proxy = {
 402    .name = TYPE_XILINX_GIC_PROXY,
 403    .version_id = 1,
 404    .minimum_version_id = 1,
 405    .minimum_version_id_old = 1,
 406    .fields = (VMStateField[]) {
 407        VMSTATE_UINT32_ARRAY(regs, GICProxy, R_MAX),
 408        VMSTATE_END_OF_LIST(),
 409    }
 410};
 411
 412static const FDTGenericGPIOSet gic_proxy_client_gpios[] = {
 413    {
 414        .names = &fdt_generic_gpio_name_set_gpio,
 415        .gpios = (FDTGenericGPIOConnection[]) {
 416            { .name = "gicp-irq",         .fdt_index = 0 },
 417            { },
 418        },
 419    },
 420    { },
 421};
 422
 423static void gic_proxy_class_init(ObjectClass *oc, void *data)
 424{
 425    DeviceClass *dc = DEVICE_CLASS(oc);
 426    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
 427    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(oc);
 428
 429    dc->reset = gic_proxy_reset;
 430    dc->realize = gic_proxy_realize;
 431    dc->vmsd = &vmstate_gic_proxy;
 432    fgic->get_irq = gic_proxy_get_irq;
 433    fggc->client_gpios = gic_proxy_client_gpios;
 434}
 435
 436static const TypeInfo gic_proxy_info = {
 437    .name          = TYPE_XILINX_GIC_PROXY,
 438    .parent        = TYPE_SYS_BUS_DEVICE,
 439    .instance_size = sizeof(GICProxy),
 440    .instance_init = gic_proxy_init,
 441    .class_init    = gic_proxy_class_init,
 442    .interfaces    = (InterfaceInfo[]) {
 443        { TYPE_FDT_GENERIC_INTC },
 444        { TYPE_FDT_GENERIC_GPIO },
 445        { },
 446    },
 447};
 448
 449static void gic_proxy_register_types(void)
 450{
 451    type_register_static(&gic_proxy_info);
 452}
 453
 454type_init(gic_proxy_register_types)
 455