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 "migration/vmstate.h"
  32#include "hw/qdev-properties.h"
  33
  34#include "qemu/bitops.h"
  35#include "qemu/log.h"
  36
  37#include "hw/fdt_generic_util.h"
  38
  39#ifndef GIC_PROXY_ERR_DEBUG
  40#define GIC_PROXY_ERR_DEBUG 0
  41#endif
  42
  43#define TYPE_XILINX_GIC_PROXY "xlnx.zynqmp-gicp"
  44
  45#define XILINX_GIC_PROXY(obj) \
  46     OBJECT_CHECK(GICProxy, (obj), TYPE_XILINX_GIC_PROXY)
  47
  48#define MAX_INTS            160
  49#define GICP_GROUPS         5
  50#define GICP_GROUP_STRIDE   0x14
  51
  52REG32(GICP0_IRQ_STATUS, 0x0)
  53REG32(GICP0_IRQ_MASK, 0x4)
  54REG32(GICP0_IRQ_ENABLE, 0x8)
  55REG32(GICP0_IRQ_DISABLE, 0xc)
  56REG32(GICP0_IRQ_TRIGGER, 0x10)
  57    #define R_GICP0_RSVD    0x000000ff
  58    #define R_GICP1_RSVD    0
  59    #define R_GICP2_RSVD    0
  60    #define R_GICP3_RSVD    0x000000ff
  61    #define R_GICP4_RSVD    0xf0000000
  62REG32(GICP_PMU_IRQ_STATUS, 0xa0)
  63REG32(GICP_PMU_IRQ_MASK, 0xa4)
  64REG32(GICP_PMU_IRQ_ENABLE, 0xa8)
  65REG32(GICP_PMU_IRQ_DISABLE, 0xac)
  66REG32(GICP_PMU_IRQ_TRIGGER, 0xb0)
  67
  68#define R_MAX (R_GICP_PMU_IRQ_TRIGGER + 1)
  69
  70typedef struct GICProxy {
  71    SysBusDevice parent_obj;
  72    MemoryRegion iomem;
  73    qemu_irq irq;
  74
  75    uint32_t pinState[GICP_GROUPS];
  76    uint32_t regs[R_MAX];
  77    RegisterInfo regs_info[R_MAX];
  78} GICProxy;
  79
  80/* Mask and status registers are needed for checking if interrupt
  81 * needs to be triggered.
  82 */
  83#define GICPN_STATUS_REG(n) ((A_GICP0_IRQ_STATUS + \
  84                             (n) * GICP_GROUP_STRIDE) >> 2)
  85#define GICPN_MASK_REG(n)   ((A_GICP0_IRQ_MASK + \
  86                             (n) * GICP_GROUP_STRIDE) >> 2)
  87
  88static void gicp_update_irq(void *opaque)
  89{
  90    GICProxy *s = XILINX_GIC_PROXY(opaque);
  91    bool pending = s->regs[R_GICP_PMU_IRQ_STATUS] &
  92                   ~s->regs[R_GICP_PMU_IRQ_MASK];
  93
  94    qemu_set_irq(s->irq, pending);
  95}
  96
  97/* Functions for handling changes to top level interrupt registers. */
  98
  99static void gicp_update(void *opaque, uint8_t nr)
 100{
 101    GICProxy *s = XILINX_GIC_PROXY(opaque);
 102
 103    if (s->regs[GICPN_STATUS_REG(nr)] & ~s->regs[GICPN_MASK_REG(nr)]) {
 104        s->regs[R_GICP_PMU_IRQ_STATUS] |= 1 << nr;
 105    } else {
 106        s->regs[R_GICP_PMU_IRQ_STATUS] &= ~(1 << nr);
 107    }
 108    gicp_update_irq(s);
 109}
 110
 111static void gicp_status_postw(RegisterInfo *reg, uint64_t val64)
 112{
 113    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 114    unsigned int i;
 115
 116    for (i = 0; i < GICP_GROUPS; i++) {
 117        gicp_update(s, i);
 118    }
 119}
 120
 121static void gicp_enable_postw(RegisterInfo *reg, uint64_t val64)
 122{
 123    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 124    uint32_t val = val64;
 125
 126    s->regs[R_GICP_PMU_IRQ_MASK] &= ~val;
 127    gicp_update_irq(s);
 128}
 129
 130static void gicp_disable_postw(RegisterInfo *reg, uint64_t val64)
 131{
 132    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 133    uint32_t val = val64;
 134
 135    s->regs[R_GICP_PMU_IRQ_MASK] |= val;
 136    gicp_update_irq(s);
 137}
 138
 139static void gicp_trigger_postw(RegisterInfo *reg, uint64_t val64)
 140{
 141    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 142    uint32_t val = val64;
 143
 144    s->regs[R_GICP_PMU_IRQ_STATUS] |= val;
 145    gicp_update_irq(s);
 146}
 147
 148/* Functions for handling changes to each interrupt register. */
 149
 150static void gicpn_status_postw(RegisterInfo *reg, uint64_t val64)
 151{
 152    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 153    uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
 154
 155    s->regs[GICPN_STATUS_REG(nr)] |= s->pinState[nr];
 156    gicp_update(s, nr);
 157}
 158
 159static void gicpn_enable_postw(RegisterInfo *reg, uint64_t val64)
 160{
 161    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 162    uint32_t val = val64;
 163    uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
 164
 165    s->regs[GICPN_MASK_REG(nr)] &= ~val;
 166    gicp_update(s, nr);
 167}
 168
 169static void gicpn_disable_postw(RegisterInfo *reg, uint64_t val64)
 170{
 171    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 172    uint32_t val = val64;
 173    uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
 174
 175    s->regs[GICPN_MASK_REG(nr)] |= val;
 176    gicp_update(s, nr);
 177}
 178
 179static void gicpn_trigger_postw(RegisterInfo *reg, uint64_t val64)
 180{
 181    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 182    uint32_t val = val64;
 183    uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
 184
 185    s->regs[GICPN_STATUS_REG(nr)] |= val;
 186    gicp_update(s, nr);
 187}
 188
 189/* Return 0 and log if reading from write-only register. */
 190static uint64_t gicp_wo_postr(RegisterInfo *reg, uint64_t val64)
 191{
 192    GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
 193    qemu_log_mask(LOG_GUEST_ERROR,
 194                  "%s: Reading from wo register at %" HWADDR_PRIx "\n",
 195                  object_get_canonical_path(OBJECT(s)),
 196                  reg->access->addr);
 197    return 0;
 198}
 199
 200/* Read-as-zero high 24 bits. */
 201static uint64_t gicp_raz_hi24_postr(RegisterInfo *reg, uint64_t val64)
 202{
 203    return val64 & 0xff;
 204}
 205
 206static RegisterAccessInfo gic_proxy_regs_info[] = {
 207#define GICPN_REG_DEFS(n) \
 208    {   .name = "GICP" #n "_IRQ_STATUS",                            \
 209        .addr = A_GICP0_IRQ_STATUS + n * GICP_GROUP_STRIDE,  \
 210        .w1c = 0xffffffff,                                          \
 211        .rsvd = R_GICP ## n ## _RSVD,                               \
 212        .post_write = gicpn_status_postw,                           \
 213    },{ .name = "GICP" #n "_IRQ_MASK",                              \
 214        .addr = A_GICP0_IRQ_MASK + n * GICP_GROUP_STRIDE,    \
 215        .ro = 0xffffffff,                                           \
 216        .rsvd = R_GICP ## n ## _RSVD,                               \
 217        .reset = 0xffffffff,                                        \
 218    },{ .name = "GICP" #n "_IRQ_ENABLE",                            \
 219        .addr = A_GICP0_IRQ_ENABLE + n * GICP_GROUP_STRIDE,  \
 220        .rsvd = R_GICP ## n ## _RSVD,                               \
 221        .post_read = gicp_wo_postr,                                 \
 222        .post_write = gicpn_enable_postw,                           \
 223    },{ .name = "GICP" #n "_IRQ_DISABLE",                           \
 224        .addr = A_GICP0_IRQ_DISABLE + n * GICP_GROUP_STRIDE, \
 225        .rsvd = R_GICP ## n ## _RSVD,                               \
 226        .post_read = gicp_wo_postr,                                 \
 227        .post_write = gicpn_disable_postw,                          \
 228    },{ .name = "GICP" #n "_IRQ_TRIGGER",                           \
 229        .addr = A_GICP0_IRQ_TRIGGER + n * GICP_GROUP_STRIDE, \
 230        .rsvd = R_GICP ## n ## _RSVD,                               \
 231        .post_read = gicp_wo_postr,                                 \
 232        .post_write = gicpn_trigger_postw,                          \
 233    }
 234    GICPN_REG_DEFS(0),
 235    GICPN_REG_DEFS(1),
 236    GICPN_REG_DEFS(2),
 237    GICPN_REG_DEFS(3),
 238    GICPN_REG_DEFS(4),
 239      { .name = "GICP_PMU_IRQ_STATUS",  .addr = A_GICP_PMU_IRQ_STATUS,
 240        .w1c = 0x000000ff,
 241        .rsvd = 0xffffffe0,
 242        .post_read = gicp_raz_hi24_postr,
 243        .post_write = gicp_status_postw,
 244    },{ .name = "GICP_PMU_IRQ_MASK",  .addr = A_GICP_PMU_IRQ_MASK,
 245        .ro = 0x000000ff,
 246        .rsvd = 0xffffffe0,
 247        .reset = 0x000000ff,
 248        .post_read = gicp_raz_hi24_postr,
 249    },{ .name = "GICP_PMU_IRQ_ENABLE",  .addr = A_GICP_PMU_IRQ_ENABLE,
 250        .rsvd = 0xffffffe0,
 251        .post_read = gicp_wo_postr,
 252        .post_write = gicp_enable_postw,
 253    },{ .name = "GICP_PMU_IRQ_DISABLE",  .addr = A_GICP_PMU_IRQ_DISABLE,
 254        .rsvd = 0xffffffe0,
 255        .post_read = gicp_wo_postr,
 256        .post_write = gicp_disable_postw,
 257    },{ .name = "GICP_PMU_IRQ_TRIGGER",  .addr = A_GICP_PMU_IRQ_TRIGGER,
 258        .rsvd = 0xffffffe0,
 259        .post_read = gicp_wo_postr,
 260        .post_write = gicp_trigger_postw,
 261    }
 262};
 263
 264static void gic_proxy_reset(DeviceState *dev)
 265{
 266    GICProxy *s = XILINX_GIC_PROXY(dev);
 267    unsigned int i;
 268
 269    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 270        register_reset(&s->regs_info[i]);
 271    }
 272}
 273
 274static const MemoryRegionOps gic_proxy_ops = {
 275    .read = register_read_memory,
 276    .write = register_write_memory,
 277    .endianness = DEVICE_LITTLE_ENDIAN,
 278    .valid = {
 279        .min_access_size = 4,
 280        .max_access_size = 4,
 281    },
 282};
 283
 284static void gic_proxy_set_irq(void *opaque, int irq, int level)
 285{
 286    GICProxy *s = XILINX_GIC_PROXY(opaque);
 287    int group = irq / 32;
 288    int bit = irq % 32;
 289
 290    if (level) {
 291        s->pinState[group] |= 1 << bit;
 292    } else {
 293        s->pinState[group] &= ~(1 << bit);
 294    }
 295    s->regs[GICPN_STATUS_REG(group)] |= s->pinState[group];
 296    gicp_update(s, group);
 297}
 298
 299static void gic_proxy_init(Object *obj)
 300{
 301    GICProxy *s = XILINX_GIC_PROXY(obj);
 302    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 303    DeviceState *dev = DEVICE(s);
 304    RegisterInfoArray *reg_array;
 305
 306    memory_region_init(&s->iomem, obj, TYPE_XILINX_GIC_PROXY, R_MAX * 4);
 307    reg_array =
 308        register_init_block32(DEVICE(obj), gic_proxy_regs_info,
 309                              ARRAY_SIZE(gic_proxy_regs_info),
 310                              s->regs_info, s->regs,
 311                              &gic_proxy_ops,
 312                              GIC_PROXY_ERR_DEBUG,
 313                              R_MAX * 4);
 314    memory_region_add_subregion(&s->iomem,
 315                                0x0,
 316                                &reg_array->mem);
 317
 318    /* IRQ grouping:
 319     * [0..31] - GICP0
 320     * [32..63] - GICP1
 321     * [64..95] - GICP2
 322     * [96..127] - GICP3
 323     * [128..159] - GICP4
 324     */
 325    qdev_init_gpio_in(dev, gic_proxy_set_irq, MAX_INTS);
 326    qdev_init_gpio_out_named(dev, &s->irq, "gicp-irq", 1);
 327
 328    sysbus_init_mmio(sbd, &s->iomem);
 329    sysbus_init_irq(sbd, &s->irq);
 330}
 331
 332static int gic_proxy_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
 333                          uint32_t *cells, int ncells, int max,
 334                          Error **errp)
 335{
 336    int idx;
 337
 338    if (ncells != 3) {
 339        error_setg(errp, "Xilinx GIC Proxy requires 3 interrupt cells, "
 340                   "%d cells given", ncells);
 341        return 0;
 342    }
 343    idx = cells[1];
 344
 345    switch (cells[0]) {
 346    case 0:
 347        if (idx >= MAX_INTS) {
 348            error_setg(errp, "Xilinx GIC Proxy has maximum index of %" PRId32
 349                       ", index %" PRId32 " given", MAX_INTS - 1, idx);
 350            return 0;
 351        }
 352        (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[1]);
 353        return 1;
 354    default:
 355        error_setg(errp, "Invalid cell 0 value in interrupt binding: %d",
 356                   cells[0]);
 357        return 0;
 358    }
 359};
 360
 361static const VMStateDescription vmstate_gic_proxy = {
 362    .name = TYPE_XILINX_GIC_PROXY,
 363    .version_id = 1,
 364    .minimum_version_id = 1,
 365    .minimum_version_id_old = 1,
 366    .fields = (VMStateField[]) {
 367        VMSTATE_UINT32_ARRAY(regs, GICProxy, R_MAX),
 368        VMSTATE_END_OF_LIST(),
 369    }
 370};
 371
 372static const FDTGenericGPIOSet gic_proxy_client_gpios[] = {
 373    {
 374        .names = &fdt_generic_gpio_name_set_gpio,
 375        .gpios = (FDTGenericGPIOConnection[]) {
 376            { .name = "gicp-irq",         .fdt_index = 0 },
 377            { },
 378        },
 379    },
 380    { },
 381};
 382
 383static void gic_proxy_class_init(ObjectClass *oc, void *data)
 384{
 385    DeviceClass *dc = DEVICE_CLASS(oc);
 386    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
 387    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(oc);
 388
 389    dc->reset = gic_proxy_reset;
 390    dc->vmsd = &vmstate_gic_proxy;
 391    fgic->get_irq = gic_proxy_get_irq;
 392    fggc->client_gpios = gic_proxy_client_gpios;
 393}
 394
 395static const TypeInfo gic_proxy_info = {
 396    .name          = TYPE_XILINX_GIC_PROXY,
 397    .parent        = TYPE_SYS_BUS_DEVICE,
 398    .instance_size = sizeof(GICProxy),
 399    .instance_init = gic_proxy_init,
 400    .class_init    = gic_proxy_class_init,
 401    .interfaces    = (InterfaceInfo[]) {
 402        { TYPE_FDT_GENERIC_INTC },
 403        { TYPE_FDT_GENERIC_GPIO },
 404        { },
 405    },
 406};
 407
 408static void gic_proxy_register_types(void)
 409{
 410    type_register_static(&gic_proxy_info);
 411}
 412
 413type_init(gic_proxy_register_types)
 414