1 /*
2 * QEMU model of the CRF - Clock Reset FPD.
3 *
4 * Copyright (c) 2022 Xilinx Inc.
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
7 */
8
9 #include "qemu/osdep.h"
10 #include "hw/sysbus.h"
11 #include "hw/register.h"
12 #include "qemu/bitops.h"
13 #include "qemu/log.h"
14 #include "migration/vmstate.h"
15 #include "hw/irq.h"
16 #include "hw/misc/xlnx-zynqmp-crf.h"
17 #include "target/arm/arm-powerctl.h"
18
19 #ifndef XLNX_ZYNQMP_CRF_ERR_DEBUG
20 #define XLNX_ZYNQMP_CRF_ERR_DEBUG 0
21 #endif
22
23 #define CRF_MAX_CPU 4
24
ir_update_irq(XlnxZynqMPCRF * s)25 static void ir_update_irq(XlnxZynqMPCRF *s)
26 {
27 bool pending = s->regs[R_IR_STATUS] & ~s->regs[R_IR_MASK];
28 qemu_set_irq(s->irq_ir, pending);
29 }
30
ir_status_postw(RegisterInfo * reg,uint64_t val64)31 static void ir_status_postw(RegisterInfo *reg, uint64_t val64)
32 {
33 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque);
34 ir_update_irq(s);
35 }
36
ir_enable_prew(RegisterInfo * reg,uint64_t val64)37 static uint64_t ir_enable_prew(RegisterInfo *reg, uint64_t val64)
38 {
39 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque);
40 uint32_t val = val64;
41
42 s->regs[R_IR_MASK] &= ~val;
43 ir_update_irq(s);
44 return 0;
45 }
46
ir_disable_prew(RegisterInfo * reg,uint64_t val64)47 static uint64_t ir_disable_prew(RegisterInfo *reg, uint64_t val64)
48 {
49 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque);
50 uint32_t val = val64;
51
52 s->regs[R_IR_MASK] |= val;
53 ir_update_irq(s);
54 return 0;
55 }
56
rst_fpd_apu_prew(RegisterInfo * reg,uint64_t val64)57 static uint64_t rst_fpd_apu_prew(RegisterInfo *reg, uint64_t val64)
58 {
59 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque);
60 uint32_t val = val64;
61 uint32_t val_old = s->regs[R_RST_FPD_APU];
62 unsigned int i;
63
64 for (i = 0; i < CRF_MAX_CPU; i++) {
65 uint32_t mask = (1 << (R_RST_FPD_APU_ACPU0_RESET_SHIFT + i));
66
67 if ((val ^ val_old) & mask) {
68 if (val & mask) {
69 arm_set_cpu_off(i);
70 } else {
71 arm_set_cpu_on_and_reset(i);
72 }
73 }
74 }
75 return val64;
76 }
77
78 static const RegisterAccessInfo crf_regs_info[] = {
79 { .name = "ERR_CTRL", .addr = A_ERR_CTRL,
80 },{ .name = "IR_STATUS", .addr = A_IR_STATUS,
81 .w1c = 0x1,
82 .post_write = ir_status_postw,
83 },{ .name = "IR_MASK", .addr = A_IR_MASK,
84 .reset = 0x1,
85 .ro = 0x1,
86 },{ .name = "IR_ENABLE", .addr = A_IR_ENABLE,
87 .pre_write = ir_enable_prew,
88 },{ .name = "IR_DISABLE", .addr = A_IR_DISABLE,
89 .pre_write = ir_disable_prew,
90 },{ .name = "CRF_WPROT", .addr = A_CRF_WPROT,
91 },{ .name = "APLL_CTRL", .addr = A_APLL_CTRL,
92 .reset = 0x12c09,
93 .rsvd = 0xf88c80f6,
94 },{ .name = "APLL_CFG", .addr = A_APLL_CFG,
95 .rsvd = 0x1801210,
96 },{ .name = "APLL_FRAC_CFG", .addr = A_APLL_FRAC_CFG,
97 .rsvd = 0x7e330000,
98 },{ .name = "DPLL_CTRL", .addr = A_DPLL_CTRL,
99 .reset = 0x2c09,
100 .rsvd = 0xf88c80f6,
101 },{ .name = "DPLL_CFG", .addr = A_DPLL_CFG,
102 .rsvd = 0x1801210,
103 },{ .name = "DPLL_FRAC_CFG", .addr = A_DPLL_FRAC_CFG,
104 .rsvd = 0x7e330000,
105 },{ .name = "VPLL_CTRL", .addr = A_VPLL_CTRL,
106 .reset = 0x12809,
107 .rsvd = 0xf88c80f6,
108 },{ .name = "VPLL_CFG", .addr = A_VPLL_CFG,
109 .rsvd = 0x1801210,
110 },{ .name = "VPLL_FRAC_CFG", .addr = A_VPLL_FRAC_CFG,
111 .rsvd = 0x7e330000,
112 },{ .name = "PLL_STATUS", .addr = A_PLL_STATUS,
113 .reset = 0x3f,
114 .rsvd = 0xc0,
115 .ro = 0x3f,
116 },{ .name = "APLL_TO_LPD_CTRL", .addr = A_APLL_TO_LPD_CTRL,
117 .reset = 0x400,
118 .rsvd = 0xc0ff,
119 },{ .name = "DPLL_TO_LPD_CTRL", .addr = A_DPLL_TO_LPD_CTRL,
120 .reset = 0x400,
121 .rsvd = 0xc0ff,
122 },{ .name = "VPLL_TO_LPD_CTRL", .addr = A_VPLL_TO_LPD_CTRL,
123 .reset = 0x400,
124 .rsvd = 0xc0ff,
125 },{ .name = "ACPU_CTRL", .addr = A_ACPU_CTRL,
126 .reset = 0x3000400,
127 .rsvd = 0xfcffc0f8,
128 },{ .name = "DBG_TRACE_CTRL", .addr = A_DBG_TRACE_CTRL,
129 .reset = 0x2500,
130 .rsvd = 0xfeffc0f8,
131 },{ .name = "DBG_FPD_CTRL", .addr = A_DBG_FPD_CTRL,
132 .reset = 0x1002500,
133 .rsvd = 0xfeffc0f8,
134 },{ .name = "DP_VIDEO_REF_CTRL", .addr = A_DP_VIDEO_REF_CTRL,
135 .reset = 0x1002300,
136 .rsvd = 0xfec0c0f8,
137 },{ .name = "DP_AUDIO_REF_CTRL", .addr = A_DP_AUDIO_REF_CTRL,
138 .reset = 0x1032300,
139 .rsvd = 0xfec0c0f8,
140 },{ .name = "DP_STC_REF_CTRL", .addr = A_DP_STC_REF_CTRL,
141 .reset = 0x1203200,
142 .rsvd = 0xfec0c0f8,
143 },{ .name = "DDR_CTRL", .addr = A_DDR_CTRL,
144 .reset = 0x1000500,
145 .rsvd = 0xfeffc0f8,
146 },{ .name = "GPU_REF_CTRL", .addr = A_GPU_REF_CTRL,
147 .reset = 0x1500,
148 .rsvd = 0xf8ffc0f8,
149 },{ .name = "SATA_REF_CTRL", .addr = A_SATA_REF_CTRL,
150 .reset = 0x1001600,
151 .rsvd = 0xfeffc0f8,
152 },{ .name = "PCIE_REF_CTRL", .addr = A_PCIE_REF_CTRL,
153 .reset = 0x1500,
154 .rsvd = 0xfeffc0f8,
155 },{ .name = "GDMA_REF_CTRL", .addr = A_GDMA_REF_CTRL,
156 .reset = 0x1000500,
157 .rsvd = 0xfeffc0f8,
158 },{ .name = "DPDMA_REF_CTRL", .addr = A_DPDMA_REF_CTRL,
159 .reset = 0x1000500,
160 .rsvd = 0xfeffc0f8,
161 },{ .name = "TOPSW_MAIN_CTRL", .addr = A_TOPSW_MAIN_CTRL,
162 .reset = 0x1000400,
163 .rsvd = 0xfeffc0f8,
164 },{ .name = "TOPSW_LSBUS_CTRL", .addr = A_TOPSW_LSBUS_CTRL,
165 .reset = 0x1000800,
166 .rsvd = 0xfeffc0f8,
167 },{ .name = "DBG_TSTMP_CTRL", .addr = A_DBG_TSTMP_CTRL,
168 .reset = 0xa00,
169 .rsvd = 0xffffc0f8,
170 },
171 { .name = "RST_FPD_TOP", .addr = A_RST_FPD_TOP,
172 .reset = 0xf9ffe,
173 .rsvd = 0xf06001,
174 },{ .name = "RST_FPD_APU", .addr = A_RST_FPD_APU,
175 .reset = 0x3d0f,
176 .rsvd = 0xc2f0,
177 .pre_write = rst_fpd_apu_prew,
178 },{ .name = "RST_DDR_SS", .addr = A_RST_DDR_SS,
179 .reset = 0xf,
180 .rsvd = 0xf3,
181 }
182 };
183
crf_reset_enter(Object * obj,ResetType type)184 static void crf_reset_enter(Object *obj, ResetType type)
185 {
186 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj);
187 unsigned int i;
188
189 for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
190 register_reset(&s->regs_info[i]);
191 }
192 }
193
crf_reset_hold(Object * obj,ResetType type)194 static void crf_reset_hold(Object *obj, ResetType type)
195 {
196 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj);
197 ir_update_irq(s);
198 }
199
200 static const MemoryRegionOps crf_ops = {
201 .read = register_read_memory,
202 .write = register_write_memory,
203 .endianness = DEVICE_LITTLE_ENDIAN,
204 .valid = {
205 .min_access_size = 4,
206 .max_access_size = 4,
207 },
208 };
209
crf_init(Object * obj)210 static void crf_init(Object *obj)
211 {
212 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj);
213 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
214
215 s->reg_array =
216 register_init_block32(DEVICE(obj), crf_regs_info,
217 ARRAY_SIZE(crf_regs_info),
218 s->regs_info, s->regs,
219 &crf_ops,
220 XLNX_ZYNQMP_CRF_ERR_DEBUG,
221 CRF_R_MAX * 4);
222 sysbus_init_mmio(sbd, &s->reg_array->mem);
223 sysbus_init_irq(sbd, &s->irq_ir);
224 }
225
crf_finalize(Object * obj)226 static void crf_finalize(Object *obj)
227 {
228 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj);
229 register_finalize_block(s->reg_array);
230 }
231
232 static const VMStateDescription vmstate_crf = {
233 .name = TYPE_XLNX_ZYNQMP_CRF,
234 .version_id = 1,
235 .minimum_version_id = 1,
236 .fields = (const VMStateField[]) {
237 VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCRF, CRF_R_MAX),
238 VMSTATE_END_OF_LIST(),
239 }
240 };
241
crf_class_init(ObjectClass * klass,void * data)242 static void crf_class_init(ObjectClass *klass, void *data)
243 {
244 ResettableClass *rc = RESETTABLE_CLASS(klass);
245 DeviceClass *dc = DEVICE_CLASS(klass);
246
247 dc->vmsd = &vmstate_crf;
248 rc->phases.enter = crf_reset_enter;
249 rc->phases.hold = crf_reset_hold;
250 }
251
252 static const TypeInfo crf_info = {
253 .name = TYPE_XLNX_ZYNQMP_CRF,
254 .parent = TYPE_SYS_BUS_DEVICE,
255 .instance_size = sizeof(XlnxZynqMPCRF),
256 .class_init = crf_class_init,
257 .instance_init = crf_init,
258 .instance_finalize = crf_finalize,
259 };
260
crf_register_types(void)261 static void crf_register_types(void)
262 {
263 type_register_static(&crf_info);
264 }
265
266 type_init(crf_register_types)
267