1 /* 2 * Nuvoton NPCM7xx/8xx 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/npcm_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 }; 70 71 static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_GCR_NR_REGS] = { 72 [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ 73 [NPCM7XX_GCR_MISCPE] = 0x0000ffff, 74 [NPCM7XX_GCR_SPSWC] = 0x00000003, 75 [NPCM7XX_GCR_INTCR] = 0x0000035e, 76 [NPCM7XX_GCR_HIFCR] = 0x0000004e, 77 [NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ 78 [NPCM7XX_GCR_RESSR] = 0x80000000, 79 [NPCM7XX_GCR_DSCNT] = 0x000000c0, 80 [NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf, 81 [NPCM7XX_GCR_SCRPAD] = 0x00000008, 82 [NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4, 83 [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, 84 }; 85 86 enum NPCM8xxGCRRegisters { 87 NPCM8XX_GCR_PDID, 88 NPCM8XX_GCR_PWRON, 89 NPCM8XX_GCR_MISCPE = 0x014 / sizeof(uint32_t), 90 NPCM8XX_GCR_FLOCKR2 = 0x020 / sizeof(uint32_t), 91 NPCM8XX_GCR_FLOCKR3, 92 NPCM8XX_GCR_A35_MODE = 0x034 / sizeof(uint32_t), 93 NPCM8XX_GCR_SPSWC, 94 NPCM8XX_GCR_INTCR, 95 NPCM8XX_GCR_INTSR, 96 NPCM8XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), 97 NPCM8XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), 98 NPCM8XX_GCR_SRCNT = 0x068 / sizeof(uint32_t), 99 NPCM8XX_GCR_RESSR, 100 NPCM8XX_GCR_RLOCKR1, 101 NPCM8XX_GCR_FLOCKR1, 102 NPCM8XX_GCR_DSCNT, 103 NPCM8XX_GCR_MDLR, 104 NPCM8XX_GCR_SCRPAD_C = 0x080 / sizeof(uint32_t), 105 NPCM8XX_GCR_SCRPAD_B, 106 NPCM8XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), 107 NPCM8XX_GCR_INTCR3, 108 NPCM8XX_GCR_PCIRCTL = 0x0a0 / sizeof(uint32_t), 109 NPCM8XX_GCR_VSINTR, 110 NPCM8XX_GCR_SD2SUR1 = 0x0b4 / sizeof(uint32_t), 111 NPCM8XX_GCR_SD2SUR2, 112 NPCM8XX_GCR_INTCR4 = 0x0c0 / sizeof(uint32_t), 113 NPCM8XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), 114 NPCM8XX_GCR_CP2BST, 115 NPCM8XX_GCR_B2CPNT, 116 NPCM8XX_GCR_CPPCTL, 117 NPCM8XX_GCR_I2CSEGSEL = 0x0e0 / sizeof(uint32_t), 118 NPCM8XX_GCR_I2CSEGCTL, 119 NPCM8XX_GCR_VSRCR, 120 NPCM8XX_GCR_MLOCKR, 121 NPCM8XX_GCR_SCRPAD = 0x13c / sizeof(uint32_t), 122 NPCM8XX_GCR_USB1PHYCTL, 123 NPCM8XX_GCR_USB2PHYCTL, 124 NPCM8XX_GCR_USB3PHYCTL, 125 NPCM8XX_GCR_MFSEL1 = 0x260 / sizeof(uint32_t), 126 NPCM8XX_GCR_MFSEL2, 127 NPCM8XX_GCR_MFSEL3, 128 NPCM8XX_GCR_MFSEL4, 129 NPCM8XX_GCR_MFSEL5, 130 NPCM8XX_GCR_MFSEL6, 131 NPCM8XX_GCR_MFSEL7, 132 NPCM8XX_GCR_MFSEL_LK1 = 0x280 / sizeof(uint32_t), 133 NPCM8XX_GCR_MFSEL_LK2, 134 NPCM8XX_GCR_MFSEL_LK3, 135 NPCM8XX_GCR_MFSEL_LK4, 136 NPCM8XX_GCR_MFSEL_LK5, 137 NPCM8XX_GCR_MFSEL_LK6, 138 NPCM8XX_GCR_MFSEL_LK7, 139 NPCM8XX_GCR_MFSEL_SET1 = 0x2a0 / sizeof(uint32_t), 140 NPCM8XX_GCR_MFSEL_SET2, 141 NPCM8XX_GCR_MFSEL_SET3, 142 NPCM8XX_GCR_MFSEL_SET4, 143 NPCM8XX_GCR_MFSEL_SET5, 144 NPCM8XX_GCR_MFSEL_SET6, 145 NPCM8XX_GCR_MFSEL_SET7, 146 NPCM8XX_GCR_MFSEL_CLR1 = 0x2c0 / sizeof(uint32_t), 147 NPCM8XX_GCR_MFSEL_CLR2, 148 NPCM8XX_GCR_MFSEL_CLR3, 149 NPCM8XX_GCR_MFSEL_CLR4, 150 NPCM8XX_GCR_MFSEL_CLR5, 151 NPCM8XX_GCR_MFSEL_CLR6, 152 NPCM8XX_GCR_MFSEL_CLR7, 153 NPCM8XX_GCR_WD0RCRLK = 0x400 / sizeof(uint32_t), 154 NPCM8XX_GCR_WD1RCRLK, 155 NPCM8XX_GCR_WD2RCRLK, 156 NPCM8XX_GCR_SWRSTC1LK, 157 NPCM8XX_GCR_SWRSTC2LK, 158 NPCM8XX_GCR_SWRSTC3LK, 159 NPCM8XX_GCR_TIPRSTCLK, 160 NPCM8XX_GCR_CORSTCLK, 161 NPCM8XX_GCR_WD0RCRBLK, 162 NPCM8XX_GCR_WD1RCRBLK, 163 NPCM8XX_GCR_WD2RCRBLK, 164 NPCM8XX_GCR_SWRSTC1BLK, 165 NPCM8XX_GCR_SWRSTC2BLK, 166 NPCM8XX_GCR_SWRSTC3BLK, 167 NPCM8XX_GCR_TIPRSTCBLK, 168 NPCM8XX_GCR_CORSTCBLK, 169 /* 64 scratch pad registers start here. 0xe00 ~ 0xefc */ 170 NPCM8XX_GCR_SCRPAD_00 = 0xe00 / sizeof(uint32_t), 171 /* 32 semaphore registers start here. 0xf00 ~ 0xf7c */ 172 NPCM8XX_GCR_GP_SEMFR_00 = 0xf00 / sizeof(uint32_t), 173 NPCM8XX_GCR_GP_SEMFR_31 = 0xf7c / sizeof(uint32_t), 174 }; 175 176 static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_GCR_NR_REGS] = { 177 [NPCM8XX_GCR_PDID] = 0x04a35850, /* Arbel A1 */ 178 [NPCM8XX_GCR_MISCPE] = 0x0000ffff, 179 [NPCM8XX_GCR_A35_MODE] = 0xfff4ff30, 180 [NPCM8XX_GCR_SPSWC] = 0x00000003, 181 [NPCM8XX_GCR_INTCR] = 0x0010035e, 182 [NPCM8XX_GCR_HIFCR] = 0x0000004e, 183 [NPCM8XX_GCR_SD2SUR1] = 0xfdc80000, 184 [NPCM8XX_GCR_SD2SUR2] = 0x5200b130, 185 [NPCM8XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ 186 [NPCM8XX_GCR_RESSR] = 0x80000000, 187 [NPCM8XX_GCR_DAVCLVLR] = 0x5a00f3cf, 188 [NPCM8XX_GCR_INTCR3] = 0x5e001002, 189 [NPCM8XX_GCR_VSRCR] = 0x00004800, 190 [NPCM8XX_GCR_SCRPAD] = 0x00000008, 191 [NPCM8XX_GCR_USB1PHYCTL] = 0x034730e4, 192 [NPCM8XX_GCR_USB2PHYCTL] = 0x034730e4, 193 [NPCM8XX_GCR_USB3PHYCTL] = 0x034730e4, 194 /* All 32 semaphores should be initialized to 1. */ 195 [NPCM8XX_GCR_GP_SEMFR_00...NPCM8XX_GCR_GP_SEMFR_31] = 0x00000001, 196 }; 197 198 static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) 199 { 200 uint32_t reg = offset / sizeof(uint32_t); 201 NPCMGCRState *s = opaque; 202 NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); 203 uint64_t value; 204 205 if (reg >= c->nr_regs) { 206 qemu_log_mask(LOG_GUEST_ERROR, 207 "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 208 __func__, offset); 209 return 0; 210 } 211 212 switch (size) { 213 case 4: 214 value = s->regs[reg]; 215 break; 216 217 case 8: 218 g_assert(!(reg & 1)); 219 value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]); 220 break; 221 222 default: 223 g_assert_not_reached(); 224 } 225 226 trace_npcm_gcr_read(offset, value); 227 return value; 228 } 229 230 static void npcm_gcr_write(void *opaque, hwaddr offset, 231 uint64_t v, unsigned size) 232 { 233 uint32_t reg = offset / sizeof(uint32_t); 234 NPCMGCRState *s = opaque; 235 NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); 236 uint32_t value = v; 237 238 trace_npcm_gcr_write(offset, v); 239 240 if (reg >= c->nr_regs) { 241 qemu_log_mask(LOG_GUEST_ERROR, 242 "%s: offset 0x%04" HWADDR_PRIx " out of range\n", 243 __func__, offset); 244 return; 245 } 246 247 switch (size) { 248 case 4: 249 switch (reg) { 250 case NPCM7XX_GCR_PDID: 251 case NPCM7XX_GCR_PWRON: 252 case NPCM7XX_GCR_INTSR: 253 qemu_log_mask(LOG_GUEST_ERROR, 254 "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 255 __func__, offset); 256 return; 257 258 case NPCM7XX_GCR_RESSR: 259 case NPCM7XX_GCR_CP2BST: 260 /* Write 1 to clear */ 261 value = s->regs[reg] & ~value; 262 break; 263 264 case NPCM7XX_GCR_RLOCKR1: 265 case NPCM7XX_GCR_MDLR: 266 /* Write 1 to set */ 267 value |= s->regs[reg]; 268 break; 269 }; 270 s->regs[reg] = value; 271 break; 272 273 case 8: 274 g_assert(!(reg & 1)); 275 s->regs[reg] = value; 276 s->regs[reg + 1] = extract64(v, 32, 32); 277 break; 278 279 default: 280 g_assert_not_reached(); 281 } 282 } 283 284 static bool npcm_gcr_check_mem_op(void *opaque, hwaddr offset, 285 unsigned size, bool is_write, 286 MemTxAttrs attrs) 287 { 288 NPCMGCRClass *c = NPCM_GCR_GET_CLASS(opaque); 289 290 if (offset >= c->nr_regs * sizeof(uint32_t)) { 291 return false; 292 } 293 294 switch (size) { 295 case 4: 296 return true; 297 case 8: 298 if (offset >= NPCM8XX_GCR_SCRPAD_00 * sizeof(uint32_t) && 299 offset < (NPCM8XX_GCR_NR_REGS - 1) * sizeof(uint32_t)) { 300 return true; 301 } else { 302 return false; 303 } 304 default: 305 return false; 306 } 307 } 308 309 static const struct MemoryRegionOps npcm_gcr_ops = { 310 .read = npcm_gcr_read, 311 .write = npcm_gcr_write, 312 .endianness = DEVICE_LITTLE_ENDIAN, 313 .valid = { 314 .min_access_size = 4, 315 .max_access_size = 8, 316 .accepts = npcm_gcr_check_mem_op, 317 .unaligned = false, 318 }, 319 }; 320 321 static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) 322 { 323 NPCMGCRState *s = NPCM_GCR(obj); 324 NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); 325 326 g_assert(sizeof(s->regs) >= sizeof(c->cold_reset_values)); 327 g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); 328 memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); 329 /* These 3 registers are at the same location in both 7xx and 8xx. */ 330 s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; 331 s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; 332 s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; 333 } 334 335 static void npcm8xx_gcr_enter_reset(Object *obj, ResetType type) 336 { 337 NPCMGCRState *s = NPCM_GCR(obj); 338 NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); 339 340 memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); 341 /* These 3 registers are at the same location in both 7xx and 8xx. */ 342 s->regs[NPCM8XX_GCR_PWRON] = s->reset_pwron; 343 s->regs[NPCM8XX_GCR_MDLR] = s->reset_mdlr; 344 s->regs[NPCM8XX_GCR_INTCR3] = s->reset_intcr3; 345 s->regs[NPCM8XX_GCR_SCRPAD_B] = s->reset_scrpad_b; 346 } 347 348 static void npcm_gcr_realize(DeviceState *dev, Error **errp) 349 { 350 ERRP_GUARD(); 351 NPCMGCRState *s = NPCM_GCR(dev); 352 uint64_t dram_size; 353 Object *obj; 354 355 obj = object_property_get_link(OBJECT(dev), "dram-mr", errp); 356 if (!obj) { 357 error_prepend(errp, "%s: required dram-mr link not found: ", __func__); 358 return; 359 } 360 dram_size = memory_region_size(MEMORY_REGION(obj)); 361 if (!is_power_of_2(dram_size) || 362 dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE || 363 dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) { 364 g_autofree char *sz = size_to_str(dram_size); 365 g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE); 366 g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE); 367 error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz); 368 error_append_hint(errp, 369 "DRAM size must be a power of two between %s and %s," 370 " inclusive.\n", min_sz, max_sz); 371 return; 372 } 373 374 /* Power-on reset value */ 375 s->reset_intcr3 = 0x00001002; 376 377 /* 378 * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the 379 * DRAM size, and is normally initialized by the boot block as part of DRAM 380 * training. However, since we don't have a complete emulation of the 381 * memory controller and try to make it look like it has already been 382 * initialized, the boot block will skip this initialization, and we need 383 * to make sure this field is set correctly up front. 384 * 385 * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of 386 * DRAM will be interpreted as 128 MiB. 387 * 388 * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 389 */ 390 s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; 391 392 /* 393 * The boot block starting from 0.0.6 for NPCM8xx SoCs stores the DRAM size 394 * in the SCRPAD2 registers. We need to set this field correctly since 395 * the initialization is skipped as we mentioned above. 396 * https://github.com/Nuvoton-Israel/u-boot/blob/npcm8mnx-v2019.01_tmp/board/nuvoton/arbel/arbel.c#L737 397 */ 398 s->reset_scrpad_b = dram_size; 399 } 400 401 static void npcm_gcr_init(Object *obj) 402 { 403 NPCMGCRState *s = NPCM_GCR(obj); 404 405 memory_region_init_io(&s->iomem, obj, &npcm_gcr_ops, s, 406 TYPE_NPCM_GCR, 4 * KiB); 407 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 408 } 409 410 static const VMStateDescription vmstate_npcm_gcr = { 411 .name = "npcm-gcr", 412 .version_id = 2, 413 .minimum_version_id = 2, 414 .fields = (const VMStateField[]) { 415 VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS), 416 VMSTATE_END_OF_LIST(), 417 }, 418 }; 419 420 static const Property npcm_gcr_properties[] = { 421 DEFINE_PROP_UINT32("disabled-modules", NPCMGCRState, reset_mdlr, 0), 422 DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0), 423 }; 424 425 static void npcm_gcr_class_init(ObjectClass *klass, void *data) 426 { 427 DeviceClass *dc = DEVICE_CLASS(klass); 428 429 dc->realize = npcm_gcr_realize; 430 dc->vmsd = &vmstate_npcm_gcr; 431 432 device_class_set_props(dc, npcm_gcr_properties); 433 } 434 435 static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) 436 { 437 NPCMGCRClass *c = NPCM_GCR_CLASS(klass); 438 DeviceClass *dc = DEVICE_CLASS(klass); 439 ResettableClass *rc = RESETTABLE_CLASS(klass); 440 441 dc->desc = "NPCM7xx System Global Control Registers"; 442 rc->phases.enter = npcm7xx_gcr_enter_reset; 443 444 c->nr_regs = NPCM7XX_GCR_NR_REGS; 445 c->cold_reset_values = npcm7xx_cold_reset_values; 446 rc->phases.enter = npcm7xx_gcr_enter_reset; 447 } 448 449 static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data) 450 { 451 NPCMGCRClass *c = NPCM_GCR_CLASS(klass); 452 DeviceClass *dc = DEVICE_CLASS(klass); 453 ResettableClass *rc = RESETTABLE_CLASS(klass); 454 455 dc->desc = "NPCM8xx System Global Control Registers"; 456 c->nr_regs = NPCM8XX_GCR_NR_REGS; 457 c->cold_reset_values = npcm8xx_cold_reset_values; 458 rc->phases.enter = npcm8xx_gcr_enter_reset; 459 } 460 461 static const TypeInfo npcm_gcr_info[] = { 462 { 463 .name = TYPE_NPCM_GCR, 464 .parent = TYPE_SYS_BUS_DEVICE, 465 .instance_size = sizeof(NPCMGCRState), 466 .instance_init = npcm_gcr_init, 467 .class_size = sizeof(NPCMGCRClass), 468 .class_init = npcm_gcr_class_init, 469 .abstract = true, 470 }, 471 { 472 .name = TYPE_NPCM7XX_GCR, 473 .parent = TYPE_NPCM_GCR, 474 .class_init = npcm7xx_gcr_class_init, 475 }, 476 { 477 .name = TYPE_NPCM8XX_GCR, 478 .parent = TYPE_NPCM_GCR, 479 .class_init = npcm8xx_gcr_class_init, 480 }, 481 }; 482 DEFINE_TYPES(npcm_gcr_info) 483