qemu/hw/intc/xilinx-zynqmp-intc-redirect.c
<<
>>
Prefs
   1/*
   2 * Implementation of Interrupt redirect component for ZynqMP ACPUs and RCPUs.
   3 * Based on power state of a CPU (one of ACPUs or RCPUs) interrupt lines going
   4 * from GIC (IRQ, FIQ, VIRQ and VFIQ for ACPUs or IRQ for RPCUs) are directed
   5 * either to PMU (OR'ed) or to the CPU.
   6 *
   7 * 2014 Aggios, Inc.
   8 *
   9 * Written by Strahinja Jankovic <strahinja.jankovic@aggios.com>
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a copy
  12 * of this software and associated documentation files (the "Software"), to deal
  13 * in the Software without restriction, including without limitation the rights
  14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15 * copies of the Software, and to permit persons to whom the Software is
  16 * furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice shall be included in
  19 * all copies or substantial portions of the Software.
  20 *
  21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27 * THE SOFTWARE.
  28 */
  29
  30#include "qemu/osdep.h"
  31#include "qapi/error.h"
  32#include "hw/sysbus.h"
  33#include "hw/register.h"
  34#include "qemu/bitops.h"
  35#include "qemu/log.h"
  36
  37#include "hw/fdt_generic_util.h"
  38
  39#define TYPE_XILINX_ZYNQMP_INTC_REDIRECT "xlnx.zynqmp-intc-redirect"
  40
  41#define XILINX_ZYNQMP_INTC_REDIRECT(obj) \
  42     OBJECT_CHECK(INTCRedirect, (obj), TYPE_XILINX_ZYNQMP_INTC_REDIRECT)
  43
  44#define NUM_LINES_FROM_GIC  4
  45
  46typedef struct INTCRedirect {
  47    /* private */
  48    DeviceState parent;
  49    /* public */
  50
  51    qemu_irq cpu_out[4];
  52    qemu_irq pmu_out;
  53
  54    bool cpu_pwrdwn_en;
  55    uint8_t irq_in;
  56
  57} INTCRedirect;
  58
  59static void intc_redirect_update_irqs(void *opaque)
  60{
  61    INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
  62    unsigned int i;
  63
  64    /* If CPU has set PWRDWN to 1, direct interrupts to PMU,
  65     * otherwise, to CPU.
  66     */
  67    if (s->cpu_pwrdwn_en) {
  68        qemu_set_irq(s->pmu_out, !!s->irq_in);
  69    } else {
  70        for (i = 0; i < NUM_LINES_FROM_GIC; i++) {
  71            qemu_set_irq(s->cpu_out[i], !!(s->irq_in & (1 << i)));
  72        }
  73    }
  74}
  75
  76static void intc_redirect_in_from_gic(void *opaque, int irq, int level)
  77{
  78    INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
  79
  80    s->irq_in = deposit32(s->irq_in, irq, 1, level);
  81    intc_redirect_update_irqs(s);
  82}
  83
  84static void intc_redirect_pwr_cntrl_enable(void *opaque, int irq, int level)
  85{
  86    INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
  87
  88    s->cpu_pwrdwn_en = level;
  89    intc_redirect_update_irqs(s);
  90}
  91
  92static void intc_redirect_init(Object *obj)
  93{
  94    INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(obj);
  95    DeviceState *dev = DEVICE(obj);
  96
  97    qdev_init_gpio_in_named(dev, intc_redirect_in_from_gic, "gic_in", 4);
  98    qdev_init_gpio_out_named(dev, s->cpu_out, "cpu_out", 4);
  99    qdev_init_gpio_out_named(dev, &s->pmu_out, "pmu_out", 1);
 100    qdev_init_gpio_in_named(dev, intc_redirect_pwr_cntrl_enable,
 101                            "cpu_pwrdwn_en", 1);
 102}
 103
 104static int intc_redirect_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
 105                          uint32_t *cells, int ncells, int max,
 106                          Error **errp)
 107{
 108    if (cells[0] >= NUM_LINES_FROM_GIC) {
 109        error_setg(errp, "ZynqMP intc redirect only supports %u interrupts,"
 110                   "index %" PRIu32 " requested", NUM_LINES_FROM_GIC, cells[0]);
 111        return 0;
 112    }
 113
 114    *irqs = qdev_get_gpio_in(DEVICE(obj), cells[0]);
 115    return 1;
 116};
 117
 118/* Interrupt GPIOs are used to connect to CPU, and regular GPIOs
 119 * for connection to PMU.
 120 */
 121static const FDTGenericGPIOSet intc_redirect_client_gpios[] = {
 122    {
 123        .names = &fdt_generic_gpio_name_set_interrupts,
 124        .gpios = (FDTGenericGPIOConnection[]) {
 125            { .name = "cpu_out",        .range = 4 },
 126            { },
 127        },
 128    },
 129    {
 130        .names = &fdt_generic_gpio_name_set_gpio,
 131        .gpios = (FDTGenericGPIOConnection[]) {
 132            { .name = "pmu_out",         .fdt_index = 0, },
 133            { .name = "cpu_pwrdwn_en",   .fdt_index = 1, },
 134            { },
 135        },
 136    },
 137    { },
 138};
 139static const FDTGenericGPIOSet intc_redirect_controller_gpios[] = {
 140    {
 141        .names = &fdt_generic_gpio_name_set_interrupts,
 142        .gpios = (FDTGenericGPIOConnection[]) {
 143            { .name = "gic_in",        .range = 4 },
 144            { },
 145        },
 146    },
 147    { },
 148};
 149
 150static void intc_redirect_class_init(ObjectClass *oc, void *data)
 151{
 152    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
 153    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(oc);
 154
 155    fgic->get_irq = intc_redirect_get_irq;
 156    fggc->client_gpios = intc_redirect_client_gpios;
 157    fggc->controller_gpios = intc_redirect_controller_gpios;
 158}
 159
 160static const TypeInfo intc_redirect_info = {
 161    .name          = TYPE_XILINX_ZYNQMP_INTC_REDIRECT,
 162    .parent        = TYPE_DEVICE,
 163    .instance_size = sizeof(INTCRedirect),
 164    .instance_init = intc_redirect_init,
 165    .class_init    = intc_redirect_class_init,
 166    .interfaces    = (InterfaceInfo[]) {
 167        { TYPE_FDT_GENERIC_INTC },
 168        { TYPE_FDT_GENERIC_GPIO },
 169        { },
 170    },
 171};
 172
 173static void intc_redirect_register_types(void)
 174{
 175    type_register_static(&intc_redirect_info);
 176}
 177
 178type_init(intc_redirect_register_types)
 179