npcm7xx_gcr.c 8.5 KB

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