qemu/hw/misc/xilinx_zynqmp_apu_ctrl.c
<<
>>
Prefs
   1/*
   2 * QEMU model of ZynqMP APU Core Functionality
   3 *
   4 * For the most part, a dummy device model.
   5 *
   6 * Copyright (c) 2013 Peter Xilinx Inc
   7 * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
   8 *
   9 * Permission is hereby granted, free of charge, to any person obtaining a copy
  10 * of this software and associated documentation files (the "Software"), to deal
  11 * in the Software without restriction, including without limitation the rights
  12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13 * copies of the Software, and to permit persons to whom the Software is
  14 * furnished to do so, subject to the following conditions:
  15 *
  16 * The above copyright notice and this permission notice shall be included in
  17 * all copies or substantial portions of the Software.
  18 *
  19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25 * THE SOFTWARE.
  26 */
  27
  28#include "qemu/osdep.h"
  29#include "qapi/error.h"
  30#include "hw/sysbus.h"
  31#include "qemu/log.h"
  32#include "cpu.h"
  33
  34#include "qemu/bitops.h"
  35#include "qapi/qmp/qerror.h"
  36#include "hw/register-dep.h"
  37#include "hw/fdt_generic_util.h"
  38
  39#ifndef ZYNQMP_APU_ERR_DEBUG
  40#define ZYNQMP_APU_ERR_DEBUG 0
  41#endif
  42
  43#define TYPE_ZYNQMP_APU "xlnx.apu"
  44
  45#define ZYNQMP_APU(obj) \
  46     OBJECT_CHECK(ZynqMPAPU, (obj), TYPE_ZYNQMP_APU)
  47
  48#ifndef XILINX_ZYNQMP_APU_ERR_DEBUG
  49#define XILINX_ZYNQMP_APU_ERR_DEBUG 0
  50#endif
  51
  52#define DB_PRINT_L(lvl, fmt, args...) do {\
  53    if (XILINX_ZYNQMP_APU_ERR_DEBUG >= lvl) {\
  54        qemu_log(TYPE_ZYNQMP_APU ": %s:" fmt, __func__, ## args);\
  55    } \
  56} while (0);
  57
  58#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
  59
  60DEP_REG32(RVBARADDR0L, 0x40)
  61DEP_REG32(RVBARADDR0H, 0x44)
  62DEP_REG32(RVBARADDR1L, 0x48)
  63DEP_REG32(RVBARADDR1H, 0x4c)
  64DEP_REG32(RVBARADDR2L, 0x50)
  65DEP_REG32(RVBARADDR2H, 0x54)
  66DEP_REG32(RVBARADDR3L, 0x58)
  67DEP_REG32(RVBARADDR3H, 0x5c)
  68DEP_REG32(PWRCTL, 0x90)
  69    DEP_FIELD(PWRCTL, CPUPWRDWNREQ, 3, 0)
  70
  71#define R_MAX ((R_PWRCTL) + 1)
  72
  73#define NUM_CPUS 4
  74
  75typedef struct ZynqMPAPU ZynqMPAPU;
  76
  77struct ZynqMPAPU {
  78    SysBusDevice busdev;
  79    MemoryRegion iomem;
  80
  81    ARMCPU *cpus[NUM_CPUS];
  82    /* WFIs towards PMU. */
  83    qemu_irq wfi_out[4];
  84    /* CPU Power status towards INTC Redirect. */
  85    qemu_irq cpu_power_status[4];
  86
  87    uint8_t cpu_pwrdwn_req;
  88    uint8_t cpu_in_wfi;
  89
  90    uint32_t regs[R_MAX];
  91    DepRegisterInfo regs_info[R_MAX];
  92};
  93
  94static void update_wfi_out(void *opaque)
  95{
  96    ZynqMPAPU *s = ZYNQMP_APU(opaque);
  97    unsigned int i, wfi_pending;
  98
  99    wfi_pending = s->cpu_pwrdwn_req & s->cpu_in_wfi;
 100    for (i = 0; i < NUM_CPUS; i++) {
 101        qemu_set_irq(s->wfi_out[i], !!(wfi_pending & (1 << i)));
 102    }
 103}
 104
 105static void zynqmp_apu_reset(DeviceState *dev)
 106{
 107    ZynqMPAPU *s = ZYNQMP_APU(dev);
 108    int i;
 109 
 110    for (i = 0; i < R_MAX; ++i) {
 111        dep_register_reset(&s->regs_info[i]);
 112    }
 113
 114    s->cpu_pwrdwn_req = 0;
 115    s->cpu_in_wfi = 0;
 116    update_wfi_out(s);
 117}
 118
 119static void zynqmp_apu_rvbar_post_write(DepRegisterInfo *reg, uint64_t val)
 120{
 121    ZynqMPAPU *s = ZYNQMP_APU(reg->opaque);
 122    int i;
 123
 124    for (i = 0; i < NUM_CPUS; ++i) {
 125        uint64_t rvbar = s->regs[R_RVBARADDR0L + 2 * i] +
 126                         ((uint64_t)s->regs[R_RVBARADDR0H + 2 * i] << 32);
 127        if (s->cpus[i]) {
 128            object_property_set_int(OBJECT(s->cpus[i]), rvbar, "rvbar",
 129                                    &error_abort);
 130            DB_PRINT("Set RVBAR %d to %" PRIx64 "\n", i, rvbar);
 131        }
 132    }
 133}
 134
 135static void zynqmp_apu_pwrctl_post_write(DepRegisterInfo *reg, uint64_t val)
 136{
 137    ZynqMPAPU *s = ZYNQMP_APU(reg->opaque);
 138    unsigned int i, new;
 139
 140    for (i = 0; i < NUM_CPUS; i++) {
 141        new = val & (1 << i);
 142        /* Check if CPU's CPUPWRDNREQ has changed. If yes, update GPIOs. */
 143        if (new != (s->cpu_pwrdwn_req & (1 << i))) {
 144            qemu_set_irq(s->cpu_power_status[i], !!new);
 145        }
 146        s->cpu_pwrdwn_req &= ~(1 << i);
 147        s->cpu_pwrdwn_req |= new;
 148    }
 149    update_wfi_out(s);
 150}
 151
 152static const DepRegisterAccessInfo zynqmp_apu_regs_info[] = {
 153#define RVBAR_REGDEF(n) \
 154    {   .name = "RVBAR CPU " #n " Low",  .decode.addr = A_RVBARADDR ## n ## L, \
 155            .reset = 0xffff0000ul,                                             \
 156            .post_write = zynqmp_apu_rvbar_post_write,                        \
 157    },{ .name = "RVBAR CPU " #n " High", .decode.addr = A_RVBARADDR ## n ## H, \
 158            .post_write = zynqmp_apu_rvbar_post_write,                        \
 159    }
 160    RVBAR_REGDEF(0),
 161    RVBAR_REGDEF(1),
 162    RVBAR_REGDEF(2),
 163    RVBAR_REGDEF(3), { .name = "PWRCTL",  .decode.addr = A_PWRCTL,
 164        .post_write = zynqmp_apu_pwrctl_post_write,
 165    }
 166};
 167
 168static const MemoryRegionOps zynqmp_apu_ops = {
 169    .read = dep_register_read_memory_le,
 170    .write = dep_register_write_memory_le,
 171    .endianness = DEVICE_LITTLE_ENDIAN,
 172    .valid = {
 173        .min_access_size = 4,
 174        .max_access_size = 4,
 175    }
 176};
 177
 178static void zynqmp_apu_handle_wfi(void *opaque, int irq, int level)
 179{
 180    ZynqMPAPU *s = ZYNQMP_APU(opaque);
 181
 182    s->cpu_in_wfi = deposit32(s->cpu_in_wfi, irq, 1, level);
 183    update_wfi_out(s);
 184}
 185
 186static void zynqmp_apu_realize(DeviceState *dev, Error **errp)
 187{
 188    ZynqMPAPU *s = ZYNQMP_APU(dev);
 189    const char *prefix = object_get_canonical_path(OBJECT(dev));
 190    int i;
 191
 192    for (i = 0; i < ARRAY_SIZE(zynqmp_apu_regs_info); ++i) {
 193        DepRegisterInfo *r = &s->regs_info[i];
 194
 195        *r = (DepRegisterInfo) {
 196            .data = (uint8_t *)&s->regs[
 197                    zynqmp_apu_regs_info[i].decode.addr/4],
 198            .data_size = sizeof(uint32_t),
 199            .access = &zynqmp_apu_regs_info[i],
 200            .debug = ZYNQMP_APU_ERR_DEBUG,
 201            .prefix = prefix,
 202            .opaque = s,
 203        };
 204        memory_region_init_io(&r->mem, OBJECT(dev), &zynqmp_apu_ops, r,
 205                              r->access->name, 4);
 206        memory_region_add_subregion(&s->iomem, r->access->decode.addr, &r->mem);
 207    }
 208    return;
 209}
 210
 211static void zynqmp_apu_init(Object *obj)
 212{
 213    ZynqMPAPU *s = ZYNQMP_APU(obj);
 214    int i;
 215
 216    memory_region_init(&s->iomem, obj, "MMIO", R_MAX * 4);
 217    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
 218
 219    for (i = 0; i < NUM_CPUS; ++i) {
 220        char *prop_name = g_strdup_printf("cpu%d", i);
 221        object_property_add_link(obj, prop_name, TYPE_ARM_CPU,
 222                                 (Object **)&s->cpus[i],
 223                                 qdev_prop_allow_set_link_before_realize,
 224                                 OBJ_PROP_LINK_UNREF_ON_RELEASE,
 225                                 &error_abort);
 226        g_free(prop_name);
 227    }
 228
 229    /* wfi_out is used to connect to PMU GPIs. */
 230    qdev_init_gpio_out_named(DEVICE(obj), s->wfi_out, "wfi_out", 4);
 231    /* CPU_POWER_STATUS is used to connect to INTC redirect. */
 232    qdev_init_gpio_out_named(DEVICE(obj), s->cpu_power_status,
 233                             "CPU_POWER_STATUS", 4);
 234    /* wfi_in is used as input from CPUs as wfi request. */
 235    qdev_init_gpio_in_named(DEVICE(obj), zynqmp_apu_handle_wfi, "wfi_in", 4);
 236}
 237
 238static const VMStateDescription vmstate_zynqmp_apu = {
 239    .name = "zynqmp_apu",
 240    .version_id = 1,
 241    .minimum_version_id = 1,
 242    .minimum_version_id_old = 1,
 243    .fields = (VMStateField[]) {
 244        VMSTATE_UINT32_ARRAY(regs, ZynqMPAPU, R_MAX),
 245        VMSTATE_END_OF_LIST(),
 246    }
 247};
 248
 249static const FDTGenericGPIOSet zynqmp_apu_controller_gpios[] = {
 250    {
 251        .names = &fdt_generic_gpio_name_set_gpio,
 252        .gpios = (FDTGenericGPIOConnection[])  {
 253            { .name = "wfi_in", .fdt_index = 0, .range = 4 },
 254            { .name = "CPU_POWER_STATUS", .fdt_index = 4, .range = 4 },
 255            { },
 256        },
 257    },
 258    { },
 259};
 260
 261static const FDTGenericGPIOSet zynqmp_apu_client_gpios[] = {
 262    {
 263        .names = &fdt_generic_gpio_name_set_gpio,
 264        .gpios = (FDTGenericGPIOConnection[])  {
 265            { .name = "wfi_out",          .fdt_index = 0, .range = 4 },
 266            { },
 267        },
 268    },
 269    { },
 270};
 271
 272static void zynqmp_apu_class_init(ObjectClass *klass, void *data)
 273{
 274    DeviceClass *dc = DEVICE_CLASS(klass);
 275    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);
 276
 277    dc->reset = zynqmp_apu_reset;
 278    dc->realize = zynqmp_apu_realize;
 279    dc->vmsd = &vmstate_zynqmp_apu;
 280    fggc->controller_gpios = zynqmp_apu_controller_gpios;
 281    fggc->client_gpios = zynqmp_apu_client_gpios;
 282}
 283
 284static const TypeInfo zynqmp_apu_info = {
 285    .name          = TYPE_ZYNQMP_APU,
 286    .parent        = TYPE_SYS_BUS_DEVICE,
 287    .instance_size = sizeof(ZynqMPAPU),
 288    .class_init    = zynqmp_apu_class_init,
 289    .instance_init = zynqmp_apu_init,
 290    .interfaces    = (InterfaceInfo[]) {
 291        { TYPE_FDT_GENERIC_GPIO },
 292        { }
 293    },
 294};
 295
 296static void zynqmp_apu_register_types(void)
 297{
 298    type_register_static(&zynqmp_apu_info);
 299}
 300
 301type_init(zynqmp_apu_register_types)
 302