aspeed_sdmc.c 8.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * ASPEED SDRAM Memory Controller
 *
 * Copyright (C) 2016 IBM Corp.
 *
 * This code is licensed under the GPL version 2 or later.  See
 * the COPYING file in the top-level directory.
 */

#include "qemu/osdep.h"
#include "qemu/log.h"
12
#include "qemu/error-report.h"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
#include "hw/misc/aspeed_sdmc.h"
#include "hw/misc/aspeed_scu.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "trace.h"

/* Protection Key Register */
#define R_PROT            (0x00 / 4)
#define   PROT_KEY_UNLOCK     0xFC600309

/* Configuration Register */
#define R_CONF            (0x04 / 4)

/*
 * Configuration register Ox4 (for Aspeed AST2400 SOC)
 *
 * These are for the record and future use. ASPEED_SDMC_DRAM_SIZE is
 * what we care about right now as it is checked by U-Boot to
 * determine the RAM size.
 */

#define ASPEED_SDMC_RESERVED            0xFFFFF800 /* 31:11 reserved */
#define ASPEED_SDMC_AST2300_COMPAT      (1 << 10)
#define ASPEED_SDMC_SCRAMBLE_PATTERN    (1 << 9)
#define ASPEED_SDMC_DATA_SCRAMBLE       (1 << 8)
#define ASPEED_SDMC_ECC_ENABLE          (1 << 7)
#define ASPEED_SDMC_VGA_COMPAT          (1 << 6) /* readonly */
#define ASPEED_SDMC_DRAM_BANK           (1 << 5)
#define ASPEED_SDMC_DRAM_BURST          (1 << 4)
#define ASPEED_SDMC_VGA_APERTURE(x)     ((x & 0x3) << 2) /* readonly */
#define     ASPEED_SDMC_VGA_8MB             0x0
#define     ASPEED_SDMC_VGA_16MB            0x1
#define     ASPEED_SDMC_VGA_32MB            0x2
#define     ASPEED_SDMC_VGA_64MB            0x3
#define ASPEED_SDMC_DRAM_SIZE(x)        (x & 0x3)
#define     ASPEED_SDMC_DRAM_64MB           0x0
#define     ASPEED_SDMC_DRAM_128MB          0x1
#define     ASPEED_SDMC_DRAM_256MB          0x2
#define     ASPEED_SDMC_DRAM_512MB          0x3

#define ASPEED_SDMC_READONLY_MASK                       \
    (ASPEED_SDMC_RESERVED | ASPEED_SDMC_VGA_COMPAT |    \
     ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB))
/*
 * Configuration register Ox4 (for Aspeed AST2500 SOC and higher)
 *
 * Incompatibilities are annotated in the list. ASPEED_SDMC_HW_VERSION
 * should be set to 1 for the AST2500 SOC.
 */
#define ASPEED_SDMC_HW_VERSION(x)       ((x & 0xf) << 28) /* readonly */
#define ASPEED_SDMC_SW_VERSION          ((x & 0xff) << 20)
#define ASPEED_SDMC_CACHE_INITIAL_DONE  (1 << 19)  /* readonly */
#define ASPEED_SDMC_AST2500_RESERVED    0x7C000 /* 18:14 reserved */
#define ASPEED_SDMC_CACHE_DDR4_CONF     (1 << 13)
#define ASPEED_SDMC_CACHE_INITIAL       (1 << 12)
#define ASPEED_SDMC_CACHE_RANGE_CTRL    (1 << 11)
#define ASPEED_SDMC_CACHE_ENABLE        (1 << 10) /* differs from AST2400 */
#define ASPEED_SDMC_DRAM_TYPE           (1 << 4)  /* differs from AST2400 */

/* DRAM size definitions differs */
#define     ASPEED_SDMC_AST2500_128MB       0x0
#define     ASPEED_SDMC_AST2500_256MB       0x1
#define     ASPEED_SDMC_AST2500_512MB       0x2
#define     ASPEED_SDMC_AST2500_1024MB      0x3

#define ASPEED_SDMC_AST2500_READONLY_MASK                               \
    (ASPEED_SDMC_HW_VERSION(0xf) | ASPEED_SDMC_CACHE_INITIAL_DONE |     \
     ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT |            \
     ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB))

static uint64_t aspeed_sdmc_read(void *opaque, hwaddr addr, unsigned size)
{
    AspeedSDMCState *s = ASPEED_SDMC(opaque);

    addr >>= 2;

    if (addr >= ARRAY_SIZE(s->regs)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
                      __func__, addr);
        return 0;
    }

    return s->regs[addr];
}

static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data,
                             unsigned int size)
{
    AspeedSDMCState *s = ASPEED_SDMC(opaque);

    addr >>= 2;

    if (addr >= ARRAY_SIZE(s->regs)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
                      __func__, addr);
        return;
    }

    if (addr != R_PROT && s->regs[R_PROT] != PROT_KEY_UNLOCK) {
        qemu_log_mask(LOG_GUEST_ERROR, "%s: SDMC is locked!\n", __func__);
        return;
    }

    if (addr == R_CONF) {
        /* Make sure readonly bits are kept */
        switch (s->silicon_rev) {
        case AST2400_A0_SILICON_REV:
            data &= ~ASPEED_SDMC_READONLY_MASK;
            break;
        case AST2500_A0_SILICON_REV:
            data &= ~ASPEED_SDMC_AST2500_READONLY_MASK;
            break;
        default:
            g_assert_not_reached();
        }
    }

    s->regs[addr] = data;
}

static const MemoryRegionOps aspeed_sdmc_ops = {
    .read = aspeed_sdmc_read,
    .write = aspeed_sdmc_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid.min_access_size = 4,
    .valid.max_access_size = 4,
};

143
static int ast2400_rambits(AspeedSDMCState *s)
144
{
145
    switch (s->ram_size >> 20) {
146 147 148 149 150 151 152 153 154 155 156 157
    case 64:
        return ASPEED_SDMC_DRAM_64MB;
    case 128:
        return ASPEED_SDMC_DRAM_128MB;
    case 256:
        return ASPEED_SDMC_DRAM_256MB;
    case 512:
        return ASPEED_SDMC_DRAM_512MB;
    default:
        break;
    }

158
    /* use a common default */
159 160 161
    error_report("warning: Invalid RAM size 0x%" PRIx64
                 ". Using default 256M", s->ram_size);
    s->ram_size = 256 << 20;
162
    return ASPEED_SDMC_DRAM_256MB;
163 164
}

165
static int ast2500_rambits(AspeedSDMCState *s)
166
{
167
    switch (s->ram_size >> 20) {
168 169 170 171 172 173 174 175 176 177 178 179
    case 128:
        return ASPEED_SDMC_AST2500_128MB;
    case 256:
        return ASPEED_SDMC_AST2500_256MB;
    case 512:
        return ASPEED_SDMC_AST2500_512MB;
    case 1024:
        return ASPEED_SDMC_AST2500_1024MB;
    default:
        break;
    }

180
    /* use a common default */
181 182 183
    error_report("warning: Invalid RAM size 0x%" PRIx64
                 ". Using default 512M", s->ram_size);
    s->ram_size = 512 << 20;
184
    return ASPEED_SDMC_AST2500_512MB;
185 186 187 188 189 190 191 192 193 194 195 196 197
}

static void aspeed_sdmc_reset(DeviceState *dev)
{
    AspeedSDMCState *s = ASPEED_SDMC(dev);

    memset(s->regs, 0, sizeof(s->regs));

    /* Set ram size bit and defaults values */
    switch (s->silicon_rev) {
    case AST2400_A0_SILICON_REV:
        s->regs[R_CONF] |=
            ASPEED_SDMC_VGA_COMPAT |
198
            ASPEED_SDMC_DRAM_SIZE(s->ram_bits);
199 200 201
        break;

    case AST2500_A0_SILICON_REV:
202
    case AST2500_A1_SILICON_REV:
203 204 205
        s->regs[R_CONF] |=
            ASPEED_SDMC_HW_VERSION(1) |
            ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) |
206
            ASPEED_SDMC_DRAM_SIZE(s->ram_bits);
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        break;

    default:
        g_assert_not_reached();
    }
}

static void aspeed_sdmc_realize(DeviceState *dev, Error **errp)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    AspeedSDMCState *s = ASPEED_SDMC(dev);

    if (!is_supported_silicon_rev(s->silicon_rev)) {
        error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
                s->silicon_rev);
        return;
    }

225 226
    switch (s->silicon_rev) {
    case AST2400_A0_SILICON_REV:
227
        s->ram_bits = ast2400_rambits(s);
228 229 230
        break;
    case AST2500_A0_SILICON_REV:
    case AST2500_A1_SILICON_REV:
231
        s->ram_bits = ast2500_rambits(s);
232 233 234 235 236
        break;
    default:
        g_assert_not_reached();
    }

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s,
                          TYPE_ASPEED_SDMC, 0x1000);
    sysbus_init_mmio(sbd, &s->iomem);
}

static const VMStateDescription vmstate_aspeed_sdmc = {
    .name = "aspeed.sdmc",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(regs, AspeedSDMCState, ASPEED_SDMC_NR_REGS),
        VMSTATE_END_OF_LIST()
    }
};

static Property aspeed_sdmc_properties[] = {
    DEFINE_PROP_UINT32("silicon-rev", AspeedSDMCState, silicon_rev, 0),
254
    DEFINE_PROP_UINT64("ram-size", AspeedSDMCState, ram_size, 0),
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    DEFINE_PROP_END_OF_LIST(),
};

static void aspeed_sdmc_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    dc->realize = aspeed_sdmc_realize;
    dc->reset = aspeed_sdmc_reset;
    dc->desc = "ASPEED SDRAM Memory Controller";
    dc->vmsd = &vmstate_aspeed_sdmc;
    dc->props = aspeed_sdmc_properties;
}

static const TypeInfo aspeed_sdmc_info = {
    .name = TYPE_ASPEED_SDMC,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(AspeedSDMCState),
    .class_init = aspeed_sdmc_class_init,
};

static void aspeed_sdmc_register_types(void)
{
    type_register_static(&aspeed_sdmc_info);
}

type_init(aspeed_sdmc_register_types);