qemu/hw/misc/allwinner-a10-dramc.c
<<
>>
Prefs
   1/*
   2 * Allwinner A10 DRAM Controller emulation
   3 *
   4 * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
   5 *
   6 *  This file is derived from Allwinner H3 DRAMC,
   7 *  by Niek Linnenbank.
   8 *
   9 * This program is free software: you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation, either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#include "qemu/osdep.h"
  24#include "qemu/units.h"
  25#include "hw/sysbus.h"
  26#include "migration/vmstate.h"
  27#include "qemu/log.h"
  28#include "qemu/module.h"
  29#include "hw/misc/allwinner-a10-dramc.h"
  30
  31/* DRAMC register offsets */
  32enum {
  33    REG_SDR_CCR = 0x0000,
  34    REG_SDR_ZQCR0 = 0x00a8,
  35    REG_SDR_ZQSR = 0x00b0
  36};
  37
  38#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
  39
  40/* DRAMC register flags */
  41enum {
  42    REG_SDR_CCR_DATA_TRAINING = (1 << 30),
  43    REG_SDR_CCR_DRAM_INIT     = (1 << 31),
  44};
  45enum {
  46    REG_SDR_ZQSR_ZCAL         = (1 << 31),
  47};
  48
  49/* DRAMC register reset values */
  50enum {
  51    REG_SDR_CCR_RESET   = 0x80020000,
  52    REG_SDR_ZQCR0_RESET = 0x07b00000,
  53    REG_SDR_ZQSR_RESET  = 0x80000000
  54};
  55
  56static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset,
  57                                       unsigned size)
  58{
  59    const AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
  60    const uint32_t idx = REG_INDEX(offset);
  61
  62    switch (offset) {
  63    case REG_SDR_CCR:
  64    case REG_SDR_ZQCR0:
  65    case REG_SDR_ZQSR:
  66        break;
  67    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
  68        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
  69                      __func__, (uint32_t)offset);
  70        return 0;
  71    default:
  72        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
  73                      __func__, (uint32_t)offset);
  74        return 0;
  75    }
  76
  77    return s->regs[idx];
  78}
  79
  80static void allwinner_a10_dramc_write(void *opaque, hwaddr offset,
  81                                   uint64_t val, unsigned size)
  82{
  83    AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
  84    const uint32_t idx = REG_INDEX(offset);
  85
  86    switch (offset) {
  87    case REG_SDR_CCR:
  88        if (val & REG_SDR_CCR_DRAM_INIT) {
  89            /* Clear DRAM_INIT to indicate process is done. */
  90            val &= ~REG_SDR_CCR_DRAM_INIT;
  91        }
  92        if (val & REG_SDR_CCR_DATA_TRAINING) {
  93            /* Clear DATA_TRAINING to indicate process is done. */
  94            val &= ~REG_SDR_CCR_DATA_TRAINING;
  95        }
  96        break;
  97    case REG_SDR_ZQCR0:
  98        /* Set ZCAL in ZQSR to indicate calibration is done. */
  99        s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL;
 100        break;
 101    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
 102        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
 103                      __func__, (uint32_t)offset);
 104        break;
 105    default:
 106        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
 107                      __func__, (uint32_t)offset);
 108        break;
 109    }
 110
 111    s->regs[idx] = (uint32_t) val;
 112}
 113
 114static const MemoryRegionOps allwinner_a10_dramc_ops = {
 115    .read = allwinner_a10_dramc_read,
 116    .write = allwinner_a10_dramc_write,
 117    .endianness = DEVICE_NATIVE_ENDIAN,
 118    .valid = {
 119        .min_access_size = 4,
 120        .max_access_size = 4,
 121    },
 122    .impl.min_access_size = 4,
 123};
 124
 125static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type)
 126{
 127    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
 128
 129    /* Set default values for registers */
 130    s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET;
 131    s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET;
 132    s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET;
 133}
 134
 135static void allwinner_a10_dramc_init(Object *obj)
 136{
 137    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 138    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
 139
 140    /* Memory mapping */
 141    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops, s,
 142                          TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE);
 143    sysbus_init_mmio(sbd, &s->iomem);
 144}
 145
 146static const VMStateDescription allwinner_a10_dramc_vmstate = {
 147    .name = "allwinner-a10-dramc",
 148    .version_id = 1,
 149    .minimum_version_id = 1,
 150    .fields = (VMStateField[]) {
 151        VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState,
 152                             AW_A10_DRAMC_REGS_NUM),
 153        VMSTATE_END_OF_LIST()
 154    }
 155};
 156
 157static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data)
 158{
 159    DeviceClass *dc = DEVICE_CLASS(klass);
 160    ResettableClass *rc = RESETTABLE_CLASS(klass);
 161
 162    rc->phases.enter = allwinner_a10_dramc_reset_enter;
 163    dc->vmsd = &allwinner_a10_dramc_vmstate;
 164}
 165
 166static const TypeInfo allwinner_a10_dramc_info = {
 167    .name          = TYPE_AW_A10_DRAMC,
 168    .parent        = TYPE_SYS_BUS_DEVICE,
 169    .instance_init = allwinner_a10_dramc_init,
 170    .instance_size = sizeof(AwA10DramControllerState),
 171    .class_init    = allwinner_a10_dramc_class_init,
 172};
 173
 174static void allwinner_a10_dramc_register(void)
 175{
 176    type_register_static(&allwinner_a10_dramc_info);
 177}
 178
 179type_init(allwinner_a10_dramc_register)
 180