qemu/hw/misc/xlnx-zynqmp-apu-ctrl.c
<<
>>
Prefs
   1/*
   2 * QEMU model of the ZynqMP APU Control.
   3 *
   4 * Copyright (c) 2013-2022 Xilinx Inc
   5 * SPDX-License-Identifier: GPL-2.0-or-later
   6 *
   7 * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> and
   8 * Edgar E. Iglesias <edgar.iglesias@xilinx.com>
   9 */
  10
  11#include "qemu/osdep.h"
  12#include "qapi/error.h"
  13#include "qemu/log.h"
  14#include "migration/vmstate.h"
  15#include "hw/qdev-properties.h"
  16#include "hw/sysbus.h"
  17#include "hw/irq.h"
  18#include "hw/register.h"
  19
  20#include "qemu/bitops.h"
  21#include "qapi/qmp/qerror.h"
  22
  23#include "hw/misc/xlnx-zynqmp-apu-ctrl.h"
  24
  25#ifndef XILINX_ZYNQMP_APU_ERR_DEBUG
  26#define XILINX_ZYNQMP_APU_ERR_DEBUG 0
  27#endif
  28
  29static void update_wfi_out(void *opaque)
  30{
  31    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque);
  32    unsigned int i, wfi_pending;
  33
  34    wfi_pending = s->cpu_pwrdwn_req & s->cpu_in_wfi;
  35    for (i = 0; i < APU_MAX_CPU; i++) {
  36        qemu_set_irq(s->wfi_out[i], !!(wfi_pending & (1 << i)));
  37    }
  38}
  39
  40static void zynqmp_apu_rvbar_post_write(RegisterInfo *reg, uint64_t val)
  41{
  42    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
  43    int i;
  44
  45    for (i = 0; i < APU_MAX_CPU; ++i) {
  46        uint64_t rvbar = s->regs[R_RVBARADDR0L + 2 * i] +
  47                         ((uint64_t)s->regs[R_RVBARADDR0H + 2 * i] << 32);
  48        if (s->cpus[i]) {
  49            object_property_set_int(OBJECT(s->cpus[i]), "rvbar", rvbar,
  50                                    &error_abort);
  51        }
  52    }
  53}
  54
  55static void zynqmp_apu_pwrctl_post_write(RegisterInfo *reg, uint64_t val)
  56{
  57    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
  58    unsigned int i, new;
  59
  60    for (i = 0; i < APU_MAX_CPU; i++) {
  61        new = val & (1 << i);
  62        /* Check if CPU's CPUPWRDNREQ has changed. If yes, update GPIOs. */
  63        if (new != (s->cpu_pwrdwn_req & (1 << i))) {
  64            qemu_set_irq(s->cpu_power_status[i], !!new);
  65        }
  66        s->cpu_pwrdwn_req &= ~(1 << i);
  67        s->cpu_pwrdwn_req |= new;
  68    }
  69    update_wfi_out(s);
  70}
  71
  72static void imr_update_irq(XlnxZynqMPAPUCtrl *s)
  73{
  74    bool pending = s->regs[R_ISR] & ~s->regs[R_IMR];
  75    qemu_set_irq(s->irq_imr, pending);
  76}
  77
  78static void isr_postw(RegisterInfo *reg, uint64_t val64)
  79{
  80    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
  81    imr_update_irq(s);
  82}
  83
  84static uint64_t ien_prew(RegisterInfo *reg, uint64_t val64)
  85{
  86    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
  87    uint32_t val = val64;
  88
  89    s->regs[R_IMR] &= ~val;
  90    imr_update_irq(s);
  91    return 0;
  92}
  93
  94static uint64_t ids_prew(RegisterInfo *reg, uint64_t val64)
  95{
  96    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(reg->opaque);
  97    uint32_t val = val64;
  98
  99    s->regs[R_IMR] |= val;
 100    imr_update_irq(s);
 101    return 0;
 102}
 103
 104static const RegisterAccessInfo zynqmp_apu_regs_info[] = {
 105#define RVBAR_REGDEF(n) \
 106    {   .name = "RVBAR CPU " #n " Low",  .addr = A_RVBARADDR ## n ## L,    \
 107            .reset = 0xffff0000ul,                                         \
 108            .post_write = zynqmp_apu_rvbar_post_write,                     \
 109    },{ .name = "RVBAR CPU " #n " High", .addr = A_RVBARADDR ## n ## H,    \
 110            .post_write = zynqmp_apu_rvbar_post_write,                     \
 111    }
 112    {   .name = "ERR_CTRL",  .addr = A_APU_ERR_CTRL,
 113    },{ .name = "ISR",  .addr = A_ISR,
 114        .w1c = 0x1,
 115        .post_write = isr_postw,
 116    },{ .name = "IMR",  .addr = A_IMR,
 117        .reset = 0x1,
 118        .ro = 0x1,
 119    },{ .name = "IEN",  .addr = A_IEN,
 120        .pre_write = ien_prew,
 121    },{ .name = "IDS",  .addr = A_IDS,
 122        .pre_write = ids_prew,
 123    },{ .name = "CONFIG_0",  .addr = A_CONFIG_0,
 124        .reset = 0xf0f,
 125    },{ .name = "CONFIG_1",  .addr = A_CONFIG_1,
 126    },
 127    RVBAR_REGDEF(0),
 128    RVBAR_REGDEF(1),
 129    RVBAR_REGDEF(2),
 130    RVBAR_REGDEF(3),
 131    { .name = "ACE_CTRL",  .addr = A_ACE_CTRL,
 132        .reset = 0xf000f,
 133    },{ .name = "SNOOP_CTRL",  .addr = A_SNOOP_CTRL,
 134    },{ .name = "PWRCTL",  .addr = A_PWRCTL,
 135        .post_write = zynqmp_apu_pwrctl_post_write,
 136    },{ .name = "PWRSTAT",  .addr = A_PWRSTAT,
 137        .ro = 0x3000f,
 138    }
 139};
 140
 141static void zynqmp_apu_reset_enter(Object *obj, ResetType type)
 142{
 143    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
 144    int i;
 145
 146    for (i = 0; i < APU_R_MAX; ++i) {
 147        register_reset(&s->regs_info[i]);
 148    }
 149
 150    s->cpu_pwrdwn_req = 0;
 151    s->cpu_in_wfi = 0;
 152}
 153
 154static void zynqmp_apu_reset_hold(Object *obj)
 155{
 156    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
 157
 158    update_wfi_out(s);
 159    imr_update_irq(s);
 160}
 161
 162static const MemoryRegionOps zynqmp_apu_ops = {
 163    .read = register_read_memory,
 164    .write = register_write_memory,
 165    .endianness = DEVICE_LITTLE_ENDIAN,
 166    .valid = {
 167        .min_access_size = 4,
 168        .max_access_size = 4,
 169    }
 170};
 171
 172static void zynqmp_apu_handle_wfi(void *opaque, int irq, int level)
 173{
 174    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(opaque);
 175
 176    s->cpu_in_wfi = deposit32(s->cpu_in_wfi, irq, 1, level);
 177    update_wfi_out(s);
 178}
 179
 180static void zynqmp_apu_init(Object *obj)
 181{
 182    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
 183    int i;
 184
 185    s->reg_array =
 186        register_init_block32(DEVICE(obj), zynqmp_apu_regs_info,
 187                              ARRAY_SIZE(zynqmp_apu_regs_info),
 188                              s->regs_info, s->regs,
 189                              &zynqmp_apu_ops,
 190                              XILINX_ZYNQMP_APU_ERR_DEBUG,
 191                              APU_R_MAX * 4);
 192    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->reg_array->mem);
 193    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq_imr);
 194
 195    for (i = 0; i < APU_MAX_CPU; ++i) {
 196        g_autofree gchar *prop_name = g_strdup_printf("cpu%d", i);
 197        object_property_add_link(obj, prop_name, TYPE_ARM_CPU,
 198                                 (Object **)&s->cpus[i],
 199                                 qdev_prop_allow_set_link_before_realize,
 200                                 OBJ_PROP_LINK_STRONG);
 201    }
 202
 203    /* wfi_out is used to connect to PMU GPIs. */
 204    qdev_init_gpio_out_named(DEVICE(obj), s->wfi_out, "wfi_out", 4);
 205    /* CPU_POWER_STATUS is used to connect to INTC redirect. */
 206    qdev_init_gpio_out_named(DEVICE(obj), s->cpu_power_status,
 207                             "CPU_POWER_STATUS", 4);
 208    /* wfi_in is used as input from CPUs as wfi request. */
 209    qdev_init_gpio_in_named(DEVICE(obj), zynqmp_apu_handle_wfi, "wfi_in", 4);
 210}
 211
 212static void zynqmp_apu_finalize(Object *obj)
 213{
 214    XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj);
 215    register_finalize_block(s->reg_array);
 216}
 217
 218static const VMStateDescription vmstate_zynqmp_apu = {
 219    .name = TYPE_XLNX_ZYNQMP_APU_CTRL,
 220    .version_id = 1,
 221    .minimum_version_id = 1,
 222    .fields = (VMStateField[]) {
 223        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPAPUCtrl, APU_R_MAX),
 224        VMSTATE_END_OF_LIST(),
 225    }
 226};
 227
 228static void zynqmp_apu_class_init(ObjectClass *klass, void *data)
 229{
 230    ResettableClass *rc = RESETTABLE_CLASS(klass);
 231    DeviceClass *dc = DEVICE_CLASS(klass);
 232
 233    dc->vmsd = &vmstate_zynqmp_apu;
 234
 235    rc->phases.enter = zynqmp_apu_reset_enter;
 236    rc->phases.hold = zynqmp_apu_reset_hold;
 237}
 238
 239static const TypeInfo zynqmp_apu_info = {
 240    .name              = TYPE_XLNX_ZYNQMP_APU_CTRL,
 241    .parent            = TYPE_SYS_BUS_DEVICE,
 242    .instance_size     = sizeof(XlnxZynqMPAPUCtrl),
 243    .class_init        = zynqmp_apu_class_init,
 244    .instance_init     = zynqmp_apu_init,
 245    .instance_finalize = zynqmp_apu_finalize,
 246};
 247
 248static void zynqmp_apu_register_types(void)
 249{
 250    type_register_static(&zynqmp_apu_info);
 251}
 252
 253type_init(zynqmp_apu_register_types)
 254