xref: /qemu/hw/misc/npcm_gcr.c (revision e4ea952fb0180e85655e9a93d39a1ad9442f76f2)
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