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