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