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
npcm_gcr_read(void * opaque,hwaddr offset,unsigned size)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
npcm_gcr_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)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
npcm_gcr_check_mem_op(void * opaque,hwaddr offset,unsigned size,bool is_write,MemTxAttrs attrs)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
npcm7xx_gcr_enter_reset(Object * obj,ResetType type)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
npcm8xx_gcr_enter_reset(Object * obj,ResetType type)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
npcm_gcr_realize(DeviceState * dev,Error ** errp)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
npcm_gcr_init(Object * obj)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
npcm_gcr_class_init(ObjectClass * klass,const void * data)425 static void npcm_gcr_class_init(ObjectClass *klass, const 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
npcm7xx_gcr_class_init(ObjectClass * klass,const void * data)435 static void npcm7xx_gcr_class_init(ObjectClass *klass, const 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
npcm8xx_gcr_class_init(ObjectClass * klass,const void * data)449 static void npcm8xx_gcr_class_init(ObjectClass *klass, const 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