qemu/hw/misc/allwinner-cpucfg.c
<<
>>
Prefs
   1/*
   2 * Allwinner CPU Configuration Module emulation
   3 *
   4 * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
   5 *
   6 * This program is free software: you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation, either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "qemu/units.h"
  22#include "hw/sysbus.h"
  23#include "migration/vmstate.h"
  24#include "qemu/log.h"
  25#include "qemu/module.h"
  26#include "qemu/error-report.h"
  27#include "qemu/timer.h"
  28#include "hw/core/cpu.h"
  29#include "target/arm/arm-powerctl.h"
  30#include "target/arm/cpu.h"
  31#include "hw/misc/allwinner-cpucfg.h"
  32#include "trace.h"
  33
  34/* CPUCFG register offsets */
  35enum {
  36    REG_CPUS_RST_CTRL       = 0x0000, /* CPUs Reset Control */
  37    REG_CPU0_RST_CTRL       = 0x0040, /* CPU#0 Reset Control */
  38    REG_CPU0_CTRL           = 0x0044, /* CPU#0 Control */
  39    REG_CPU0_STATUS         = 0x0048, /* CPU#0 Status */
  40    REG_CPU1_RST_CTRL       = 0x0080, /* CPU#1 Reset Control */
  41    REG_CPU1_CTRL           = 0x0084, /* CPU#1 Control */
  42    REG_CPU1_STATUS         = 0x0088, /* CPU#1 Status */
  43    REG_CPU2_RST_CTRL       = 0x00C0, /* CPU#2 Reset Control */
  44    REG_CPU2_CTRL           = 0x00C4, /* CPU#2 Control */
  45    REG_CPU2_STATUS         = 0x00C8, /* CPU#2 Status */
  46    REG_CPU3_RST_CTRL       = 0x0100, /* CPU#3 Reset Control */
  47    REG_CPU3_CTRL           = 0x0104, /* CPU#3 Control */
  48    REG_CPU3_STATUS         = 0x0108, /* CPU#3 Status */
  49    REG_CPU_SYS_RST         = 0x0140, /* CPU System Reset */
  50    REG_CLK_GATING          = 0x0144, /* CPU Clock Gating */
  51    REG_GEN_CTRL            = 0x0184, /* General Control */
  52    REG_SUPER_STANDBY       = 0x01A0, /* Super Standby Flag */
  53    REG_ENTRY_ADDR          = 0x01A4, /* Reset Entry Address */
  54    REG_DBG_EXTERN          = 0x01E4, /* Debug External */
  55    REG_CNT64_CTRL          = 0x0280, /* 64-bit Counter Control */
  56    REG_CNT64_LOW           = 0x0284, /* 64-bit Counter Low */
  57    REG_CNT64_HIGH          = 0x0288, /* 64-bit Counter High */
  58};
  59
  60/* CPUCFG register flags */
  61enum {
  62    CPUX_RESET_RELEASED     = ((1 << 1) | (1 << 0)),
  63    CPUX_STATUS_SMP         = (1 << 0),
  64    CPU_SYS_RESET_RELEASED  = (1 << 0),
  65    CLK_GATING_ENABLE       = ((1 << 8) | 0xF),
  66};
  67
  68/* CPUCFG register reset values */
  69enum {
  70    REG_CLK_GATING_RST      = 0x0000010F,
  71    REG_GEN_CTRL_RST        = 0x00000020,
  72    REG_SUPER_STANDBY_RST   = 0x0,
  73    REG_CNT64_CTRL_RST      = 0x0,
  74};
  75
  76/* CPUCFG constants */
  77enum {
  78    CPU_EXCEPTION_LEVEL_ON_RESET = 3, /* EL3 */
  79};
  80
  81static void allwinner_cpucfg_cpu_reset(AwCpuCfgState *s, uint8_t cpu_id)
  82{
  83    int ret;
  84
  85    trace_allwinner_cpucfg_cpu_reset(cpu_id, s->entry_addr);
  86
  87    ARMCPU *target_cpu = ARM_CPU(arm_get_cpu_by_id(cpu_id));
  88    if (!target_cpu) {
  89        /*
  90         * Called with a bogus value for cpu_id. Guest error will
  91         * already have been logged, we can simply return here.
  92         */
  93        return;
  94    }
  95    bool target_aa64 = arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64);
  96
  97    ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0,
  98                         CPU_EXCEPTION_LEVEL_ON_RESET, target_aa64);
  99    if (ret != QEMU_ARM_POWERCTL_RET_SUCCESS) {
 100        error_report("%s: failed to bring up CPU %d: err %d",
 101                     __func__, cpu_id, ret);
 102        return;
 103    }
 104}
 105
 106static uint64_t allwinner_cpucfg_read(void *opaque, hwaddr offset,
 107                                      unsigned size)
 108{
 109    const AwCpuCfgState *s = AW_CPUCFG(opaque);
 110    uint64_t val = 0;
 111
 112    switch (offset) {
 113    case REG_CPUS_RST_CTRL:     /* CPUs Reset Control */
 114    case REG_CPU_SYS_RST:       /* CPU System Reset */
 115        val = CPU_SYS_RESET_RELEASED;
 116        break;
 117    case REG_CPU0_RST_CTRL:     /* CPU#0 Reset Control */
 118    case REG_CPU1_RST_CTRL:     /* CPU#1 Reset Control */
 119    case REG_CPU2_RST_CTRL:     /* CPU#2 Reset Control */
 120    case REG_CPU3_RST_CTRL:     /* CPU#3 Reset Control */
 121        val = CPUX_RESET_RELEASED;
 122        break;
 123    case REG_CPU0_CTRL:         /* CPU#0 Control */
 124    case REG_CPU1_CTRL:         /* CPU#1 Control */
 125    case REG_CPU2_CTRL:         /* CPU#2 Control */
 126    case REG_CPU3_CTRL:         /* CPU#3 Control */
 127        val = 0;
 128        break;
 129    case REG_CPU0_STATUS:       /* CPU#0 Status */
 130    case REG_CPU1_STATUS:       /* CPU#1 Status */
 131    case REG_CPU2_STATUS:       /* CPU#2 Status */
 132    case REG_CPU3_STATUS:       /* CPU#3 Status */
 133        val = CPUX_STATUS_SMP;
 134        break;
 135    case REG_CLK_GATING:        /* CPU Clock Gating */
 136        val = CLK_GATING_ENABLE;
 137        break;
 138    case REG_GEN_CTRL:          /* General Control */
 139        val = s->gen_ctrl;
 140        break;
 141    case REG_SUPER_STANDBY:     /* Super Standby Flag */
 142        val = s->super_standby;
 143        break;
 144    case REG_ENTRY_ADDR:        /* Reset Entry Address */
 145        val = s->entry_addr;
 146        break;
 147    case REG_DBG_EXTERN:        /* Debug External */
 148    case REG_CNT64_CTRL:        /* 64-bit Counter Control */
 149    case REG_CNT64_LOW:         /* 64-bit Counter Low */
 150    case REG_CNT64_HIGH:        /* 64-bit Counter High */
 151        qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n",
 152                      __func__, (uint32_t)offset);
 153        break;
 154    default:
 155        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
 156                      __func__, (uint32_t)offset);
 157        break;
 158    }
 159
 160    trace_allwinner_cpucfg_read(offset, val, size);
 161
 162    return val;
 163}
 164
 165static void allwinner_cpucfg_write(void *opaque, hwaddr offset,
 166                                   uint64_t val, unsigned size)
 167{
 168    AwCpuCfgState *s = AW_CPUCFG(opaque);
 169
 170    trace_allwinner_cpucfg_write(offset, val, size);
 171
 172    switch (offset) {
 173    case REG_CPUS_RST_CTRL:     /* CPUs Reset Control */
 174    case REG_CPU_SYS_RST:       /* CPU System Reset */
 175        break;
 176    case REG_CPU0_RST_CTRL:     /* CPU#0 Reset Control */
 177    case REG_CPU1_RST_CTRL:     /* CPU#1 Reset Control */
 178    case REG_CPU2_RST_CTRL:     /* CPU#2 Reset Control */
 179    case REG_CPU3_RST_CTRL:     /* CPU#3 Reset Control */
 180        if (val) {
 181            allwinner_cpucfg_cpu_reset(s, (offset - REG_CPU0_RST_CTRL) >> 6);
 182        }
 183        break;
 184    case REG_CPU0_CTRL:         /* CPU#0 Control */
 185    case REG_CPU1_CTRL:         /* CPU#1 Control */
 186    case REG_CPU2_CTRL:         /* CPU#2 Control */
 187    case REG_CPU3_CTRL:         /* CPU#3 Control */
 188    case REG_CPU0_STATUS:       /* CPU#0 Status */
 189    case REG_CPU1_STATUS:       /* CPU#1 Status */
 190    case REG_CPU2_STATUS:       /* CPU#2 Status */
 191    case REG_CPU3_STATUS:       /* CPU#3 Status */
 192    case REG_CLK_GATING:        /* CPU Clock Gating */
 193        break;
 194    case REG_GEN_CTRL:          /* General Control */
 195        s->gen_ctrl = val;
 196        break;
 197    case REG_SUPER_STANDBY:     /* Super Standby Flag */
 198        s->super_standby = val;
 199        break;
 200    case REG_ENTRY_ADDR:        /* Reset Entry Address */
 201        s->entry_addr = val;
 202        break;
 203    case REG_DBG_EXTERN:        /* Debug External */
 204    case REG_CNT64_CTRL:        /* 64-bit Counter Control */
 205    case REG_CNT64_LOW:         /* 64-bit Counter Low */
 206    case REG_CNT64_HIGH:        /* 64-bit Counter High */
 207        qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n",
 208                      __func__, (uint32_t)offset);
 209        break;
 210    default:
 211        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
 212                      __func__, (uint32_t)offset);
 213        break;
 214    }
 215}
 216
 217static const MemoryRegionOps allwinner_cpucfg_ops = {
 218    .read = allwinner_cpucfg_read,
 219    .write = allwinner_cpucfg_write,
 220    .endianness = DEVICE_NATIVE_ENDIAN,
 221    .valid = {
 222        .min_access_size = 4,
 223        .max_access_size = 4,
 224    },
 225    .impl.min_access_size = 4,
 226};
 227
 228static void allwinner_cpucfg_reset(DeviceState *dev)
 229{
 230    AwCpuCfgState *s = AW_CPUCFG(dev);
 231
 232    /* Set default values for registers */
 233    s->gen_ctrl = REG_GEN_CTRL_RST;
 234    s->super_standby = REG_SUPER_STANDBY_RST;
 235    s->entry_addr = 0;
 236}
 237
 238static void allwinner_cpucfg_init(Object *obj)
 239{
 240    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 241    AwCpuCfgState *s = AW_CPUCFG(obj);
 242
 243    /* Memory mapping */
 244    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_cpucfg_ops, s,
 245                          TYPE_AW_CPUCFG, 1 * KiB);
 246    sysbus_init_mmio(sbd, &s->iomem);
 247}
 248
 249static const VMStateDescription allwinner_cpucfg_vmstate = {
 250    .name = "allwinner-cpucfg",
 251    .version_id = 1,
 252    .minimum_version_id = 1,
 253    .fields = (VMStateField[]) {
 254        VMSTATE_UINT32(gen_ctrl, AwCpuCfgState),
 255        VMSTATE_UINT32(super_standby, AwCpuCfgState),
 256        VMSTATE_UINT32(entry_addr, AwCpuCfgState),
 257        VMSTATE_END_OF_LIST()
 258    }
 259};
 260
 261static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data)
 262{
 263    DeviceClass *dc = DEVICE_CLASS(klass);
 264
 265    dc->reset = allwinner_cpucfg_reset;
 266    dc->vmsd = &allwinner_cpucfg_vmstate;
 267}
 268
 269static const TypeInfo allwinner_cpucfg_info = {
 270    .name          = TYPE_AW_CPUCFG,
 271    .parent        = TYPE_SYS_BUS_DEVICE,
 272    .instance_init = allwinner_cpucfg_init,
 273    .instance_size = sizeof(AwCpuCfgState),
 274    .class_init    = allwinner_cpucfg_class_init,
 275};
 276
 277static void allwinner_cpucfg_register(void)
 278{
 279    type_register_static(&allwinner_cpucfg_info);
 280}
 281
 282type_init(allwinner_cpucfg_register)
 283