1e5a7ba87SHavard Skinnemoen /* 2e5a7ba87SHavard Skinnemoen * Nuvoton NPCM7xx System Global Control Registers. 3e5a7ba87SHavard Skinnemoen * 4e5a7ba87SHavard Skinnemoen * Copyright 2020 Google LLC 5e5a7ba87SHavard Skinnemoen * 6e5a7ba87SHavard Skinnemoen * This program is free software; you can redistribute it and/or modify it 7e5a7ba87SHavard Skinnemoen * under the terms of the GNU General Public License as published by the 8e5a7ba87SHavard Skinnemoen * Free Software Foundation; either version 2 of the License, or 9e5a7ba87SHavard Skinnemoen * (at your option) any later version. 10e5a7ba87SHavard Skinnemoen * 11e5a7ba87SHavard Skinnemoen * This program is distributed in the hope that it will be useful, but WITHOUT 12e5a7ba87SHavard Skinnemoen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13e5a7ba87SHavard Skinnemoen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14e5a7ba87SHavard Skinnemoen * for more details. 15e5a7ba87SHavard Skinnemoen */ 16e5a7ba87SHavard Skinnemoen 17e5a7ba87SHavard Skinnemoen #include "qemu/osdep.h" 18e5a7ba87SHavard Skinnemoen 19e5a7ba87SHavard Skinnemoen #include "hw/misc/npcm7xx_gcr.h" 20e5a7ba87SHavard Skinnemoen #include "hw/qdev-properties.h" 21e5a7ba87SHavard Skinnemoen #include "migration/vmstate.h" 22e5a7ba87SHavard Skinnemoen #include "qapi/error.h" 23e5a7ba87SHavard Skinnemoen #include "qemu/cutils.h" 24e5a7ba87SHavard Skinnemoen #include "qemu/log.h" 25e5a7ba87SHavard Skinnemoen #include "qemu/module.h" 26e5a7ba87SHavard Skinnemoen #include "qemu/units.h" 27e5a7ba87SHavard Skinnemoen 28e5a7ba87SHavard Skinnemoen #include "trace.h" 29e5a7ba87SHavard Skinnemoen 30e5a7ba87SHavard Skinnemoen #define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB) 31e5a7ba87SHavard Skinnemoen #define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB) 32e5a7ba87SHavard Skinnemoen 33e5a7ba87SHavard Skinnemoen enum NPCM7xxGCRRegisters { 34e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_PDID, 35e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_PWRON, 36e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t), 37e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MFSEL2, 38e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MISCPE, 39e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t), 40e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_INTCR, 41e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_INTSR, 42e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), 43e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), 44e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MFSEL3, 45e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_SRCNT, 46e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_RESSR, 47e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_RLOCKR1, 48e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_FLOCKR1, 49e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_DSCNT, 50e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MDLR, 51e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_SCRPAD3, 52e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_SCRPAD2, 53e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), 54e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_INTCR3, 55e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t), 56e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MFSEL4, 57e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t), 58e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), 59e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_CP2BST, 60e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_B2CPNT, 61e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_CPPCTL, 62e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_I2CSEGSEL, 63e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_I2CSEGCTL, 64e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_VSRCR, 65e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_MLOCKR, 66e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t), 67e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_USB1PHYCTL, 68e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_USB2PHYCTL, 69e5a7ba87SHavard Skinnemoen NPCM7XX_GCR_REGS_END, 70e5a7ba87SHavard Skinnemoen }; 71e5a7ba87SHavard Skinnemoen 72e5a7ba87SHavard Skinnemoen static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = { 73e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ 74e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_MISCPE] = 0x0000ffff, 75e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_SPSWC] = 0x00000003, 76e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_INTCR] = 0x0000035e, 77e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_HIFCR] = 0x0000004e, 78e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ 79e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_RESSR] = 0x80000000, 80e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_DSCNT] = 0x000000c0, 81e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf, 82e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_SCRPAD] = 0x00000008, 83e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4, 84e5a7ba87SHavard Skinnemoen [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, 85e5a7ba87SHavard Skinnemoen }; 86e5a7ba87SHavard Skinnemoen 87e5a7ba87SHavard Skinnemoen static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size) 88e5a7ba87SHavard Skinnemoen { 89e5a7ba87SHavard Skinnemoen uint32_t reg = offset / sizeof(uint32_t); 90e5a7ba87SHavard Skinnemoen NPCM7xxGCRState *s = opaque; 91e5a7ba87SHavard Skinnemoen 92e5a7ba87SHavard Skinnemoen if (reg >= NPCM7XX_GCR_NR_REGS) { 93e5a7ba87SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 94e5a7ba87SHavard Skinnemoen "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 95e5a7ba87SHavard Skinnemoen __func__, offset); 96e5a7ba87SHavard Skinnemoen return 0; 97e5a7ba87SHavard Skinnemoen } 98e5a7ba87SHavard Skinnemoen 99e5a7ba87SHavard Skinnemoen trace_npcm7xx_gcr_read(offset, s->regs[reg]); 100e5a7ba87SHavard Skinnemoen 101e5a7ba87SHavard Skinnemoen return s->regs[reg]; 102e5a7ba87SHavard Skinnemoen } 103e5a7ba87SHavard Skinnemoen 104e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_write(void *opaque, hwaddr offset, 105e5a7ba87SHavard Skinnemoen uint64_t v, unsigned size) 106e5a7ba87SHavard Skinnemoen { 107e5a7ba87SHavard Skinnemoen uint32_t reg = offset / sizeof(uint32_t); 108e5a7ba87SHavard Skinnemoen NPCM7xxGCRState *s = opaque; 109e5a7ba87SHavard Skinnemoen uint32_t value = v; 110e5a7ba87SHavard Skinnemoen 111e5a7ba87SHavard Skinnemoen trace_npcm7xx_gcr_write(offset, value); 112e5a7ba87SHavard Skinnemoen 113e5a7ba87SHavard Skinnemoen if (reg >= NPCM7XX_GCR_NR_REGS) { 114e5a7ba87SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 115e5a7ba87SHavard Skinnemoen "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 116e5a7ba87SHavard Skinnemoen __func__, offset); 117e5a7ba87SHavard Skinnemoen return; 118e5a7ba87SHavard Skinnemoen } 119e5a7ba87SHavard Skinnemoen 120e5a7ba87SHavard Skinnemoen switch (reg) { 121e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_PDID: 122e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_PWRON: 123e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_INTSR: 124e5a7ba87SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 125e5a7ba87SHavard Skinnemoen "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 126e5a7ba87SHavard Skinnemoen __func__, offset); 127e5a7ba87SHavard Skinnemoen return; 128e5a7ba87SHavard Skinnemoen 129e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_RESSR: 130e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_CP2BST: 131e5a7ba87SHavard Skinnemoen /* Write 1 to clear */ 132e5a7ba87SHavard Skinnemoen value = s->regs[reg] & ~value; 133e5a7ba87SHavard Skinnemoen break; 134e5a7ba87SHavard Skinnemoen 135e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_RLOCKR1: 136e5a7ba87SHavard Skinnemoen case NPCM7XX_GCR_MDLR: 137e5a7ba87SHavard Skinnemoen /* Write 1 to set */ 138e5a7ba87SHavard Skinnemoen value |= s->regs[reg]; 139e5a7ba87SHavard Skinnemoen break; 140e5a7ba87SHavard Skinnemoen }; 141e5a7ba87SHavard Skinnemoen 142e5a7ba87SHavard Skinnemoen s->regs[reg] = value; 143e5a7ba87SHavard Skinnemoen } 144e5a7ba87SHavard Skinnemoen 145e5a7ba87SHavard Skinnemoen static const struct MemoryRegionOps npcm7xx_gcr_ops = { 146e5a7ba87SHavard Skinnemoen .read = npcm7xx_gcr_read, 147e5a7ba87SHavard Skinnemoen .write = npcm7xx_gcr_write, 148e5a7ba87SHavard Skinnemoen .endianness = DEVICE_LITTLE_ENDIAN, 149e5a7ba87SHavard Skinnemoen .valid = { 150e5a7ba87SHavard Skinnemoen .min_access_size = 4, 151e5a7ba87SHavard Skinnemoen .max_access_size = 4, 152e5a7ba87SHavard Skinnemoen .unaligned = false, 153e5a7ba87SHavard Skinnemoen }, 154e5a7ba87SHavard Skinnemoen }; 155e5a7ba87SHavard Skinnemoen 156e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) 157e5a7ba87SHavard Skinnemoen { 158e5a7ba87SHavard Skinnemoen NPCM7xxGCRState *s = NPCM7XX_GCR(obj); 159e5a7ba87SHavard Skinnemoen 160e5a7ba87SHavard Skinnemoen QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); 161e5a7ba87SHavard Skinnemoen 162e5a7ba87SHavard Skinnemoen switch (type) { 163e5a7ba87SHavard Skinnemoen case RESET_TYPE_COLD: 164e5a7ba87SHavard Skinnemoen memcpy(s->regs, cold_reset_values, sizeof(s->regs)); 165e5a7ba87SHavard Skinnemoen s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; 166e5a7ba87SHavard Skinnemoen s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; 167e5a7ba87SHavard Skinnemoen s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; 168e5a7ba87SHavard Skinnemoen break; 169e5a7ba87SHavard Skinnemoen } 170e5a7ba87SHavard Skinnemoen } 171e5a7ba87SHavard Skinnemoen 172e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) 173e5a7ba87SHavard Skinnemoen { 174e5a7ba87SHavard Skinnemoen ERRP_GUARD(); 175e5a7ba87SHavard Skinnemoen NPCM7xxGCRState *s = NPCM7XX_GCR(dev); 176e5a7ba87SHavard Skinnemoen uint64_t dram_size; 177e5a7ba87SHavard Skinnemoen Object *obj; 178e5a7ba87SHavard Skinnemoen 179e5a7ba87SHavard Skinnemoen obj = object_property_get_link(OBJECT(dev), "dram-mr", errp); 180e5a7ba87SHavard Skinnemoen if (!obj) { 181e5a7ba87SHavard Skinnemoen error_prepend(errp, "%s: required dram-mr link not found: ", __func__); 182e5a7ba87SHavard Skinnemoen return; 183e5a7ba87SHavard Skinnemoen } 184e5a7ba87SHavard Skinnemoen dram_size = memory_region_size(MEMORY_REGION(obj)); 185e5a7ba87SHavard Skinnemoen if (!is_power_of_2(dram_size) || 186e5a7ba87SHavard Skinnemoen dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE || 187e5a7ba87SHavard Skinnemoen dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) { 188e5a7ba87SHavard Skinnemoen g_autofree char *sz = size_to_str(dram_size); 189e5a7ba87SHavard Skinnemoen g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE); 190e5a7ba87SHavard Skinnemoen g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE); 191e5a7ba87SHavard Skinnemoen error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz); 192e5a7ba87SHavard Skinnemoen error_append_hint(errp, 193e5a7ba87SHavard Skinnemoen "DRAM size must be a power of two between %s and %s," 194e5a7ba87SHavard Skinnemoen " inclusive.\n", min_sz, max_sz); 195e5a7ba87SHavard Skinnemoen return; 196e5a7ba87SHavard Skinnemoen } 197e5a7ba87SHavard Skinnemoen 198e5a7ba87SHavard Skinnemoen /* Power-on reset value */ 199e5a7ba87SHavard Skinnemoen s->reset_intcr3 = 0x00001002; 200e5a7ba87SHavard Skinnemoen 201e5a7ba87SHavard Skinnemoen /* 202e5a7ba87SHavard Skinnemoen * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the 203e5a7ba87SHavard Skinnemoen * DRAM size, and is normally initialized by the boot block as part of DRAM 204e5a7ba87SHavard Skinnemoen * training. However, since we don't have a complete emulation of the 205e5a7ba87SHavard Skinnemoen * memory controller and try to make it look like it has already been 206e5a7ba87SHavard Skinnemoen * initialized, the boot block will skip this initialization, and we need 207e5a7ba87SHavard Skinnemoen * to make sure this field is set correctly up front. 208e5a7ba87SHavard Skinnemoen * 209e5a7ba87SHavard Skinnemoen * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of 210e5a7ba87SHavard Skinnemoen * DRAM will be interpreted as 128 MiB. 211e5a7ba87SHavard Skinnemoen * 212e5a7ba87SHavard Skinnemoen * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 213e5a7ba87SHavard Skinnemoen */ 214e5a7ba87SHavard Skinnemoen s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; 215e5a7ba87SHavard Skinnemoen } 216e5a7ba87SHavard Skinnemoen 217e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_init(Object *obj) 218e5a7ba87SHavard Skinnemoen { 219e5a7ba87SHavard Skinnemoen NPCM7xxGCRState *s = NPCM7XX_GCR(obj); 220e5a7ba87SHavard Skinnemoen 221e5a7ba87SHavard Skinnemoen memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s, 222e5a7ba87SHavard Skinnemoen TYPE_NPCM7XX_GCR, 4 * KiB); 223828d651cSHao Wu sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 224e5a7ba87SHavard Skinnemoen } 225e5a7ba87SHavard Skinnemoen 226e5a7ba87SHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_gcr = { 227e5a7ba87SHavard Skinnemoen .name = "npcm7xx-gcr", 228e5a7ba87SHavard Skinnemoen .version_id = 0, 229e5a7ba87SHavard Skinnemoen .minimum_version_id = 0, 230e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 231e5a7ba87SHavard Skinnemoen VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS), 232e5a7ba87SHavard Skinnemoen VMSTATE_END_OF_LIST(), 233e5a7ba87SHavard Skinnemoen }, 234e5a7ba87SHavard Skinnemoen }; 235e5a7ba87SHavard Skinnemoen 236e5a7ba87SHavard Skinnemoen static Property npcm7xx_gcr_properties[] = { 237e5a7ba87SHavard Skinnemoen DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0), 238e5a7ba87SHavard Skinnemoen DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0), 239e5a7ba87SHavard Skinnemoen DEFINE_PROP_END_OF_LIST(), 240e5a7ba87SHavard Skinnemoen }; 241e5a7ba87SHavard Skinnemoen 242e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) 243e5a7ba87SHavard Skinnemoen { 244e5a7ba87SHavard Skinnemoen ResettableClass *rc = RESETTABLE_CLASS(klass); 245e5a7ba87SHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 246e5a7ba87SHavard Skinnemoen 247e5a7ba87SHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS); 248e5a7ba87SHavard Skinnemoen 249e5a7ba87SHavard Skinnemoen dc->desc = "NPCM7xx System Global Control Registers"; 250e5a7ba87SHavard Skinnemoen dc->realize = npcm7xx_gcr_realize; 251e5a7ba87SHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_gcr; 252e5a7ba87SHavard Skinnemoen rc->phases.enter = npcm7xx_gcr_enter_reset; 253e5a7ba87SHavard Skinnemoen 254e5a7ba87SHavard Skinnemoen device_class_set_props(dc, npcm7xx_gcr_properties); 255e5a7ba87SHavard Skinnemoen } 256e5a7ba87SHavard Skinnemoen 257e5a7ba87SHavard Skinnemoen static const TypeInfo npcm7xx_gcr_info = { 258e5a7ba87SHavard Skinnemoen .name = TYPE_NPCM7XX_GCR, 259e5a7ba87SHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 260e5a7ba87SHavard Skinnemoen .instance_size = sizeof(NPCM7xxGCRState), 261e5a7ba87SHavard Skinnemoen .instance_init = npcm7xx_gcr_init, 262e5a7ba87SHavard Skinnemoen .class_init = npcm7xx_gcr_class_init, 263e5a7ba87SHavard Skinnemoen }; 264e5a7ba87SHavard Skinnemoen 265e5a7ba87SHavard Skinnemoen static void npcm7xx_gcr_register_type(void) 266e5a7ba87SHavard Skinnemoen { 267e5a7ba87SHavard Skinnemoen type_register_static(&npcm7xx_gcr_info); 268e5a7ba87SHavard Skinnemoen } 269e5a7ba87SHavard Skinnemoen type_init(npcm7xx_gcr_register_type); 270