@samitouri / QOSamiQemu / commits / dd0f400125

hw/misc: Add dummy ZYNQ DDR controller

A dummy DDR controller for ZYNQ has been added. While all registers are present, not all are functional. Read and write access is validated, and the user mode can be set. This provides a basic DDR controller initialization, preventing system hangs due to endless polling or similar issues. Signed-off-by: YannickV <Y.Vossen@beckhoff.com> Message-id: 20260518073401.11279-7-corvin.koehne@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

YannickV committed May 18, 2026 at 09:33 UTC dd0f400125b420dce745342616fa240ad9f46412
7 files changed +588 -3
MAINTAINERS
+2
@@ -1144,8 +1144,10 @@ L: qemu-arm@nongnu.org
1144 S: Maintained
1145 F: hw/*/xilinx_*
1146 F: hw/*/cadence_*
1147 +F: hw/misc/xlnx-zynq-ddrc.c
1148 F: hw/misc/zynq_slcr.c
1149 F: hw/adc/zynq-xadc.c
1150 +F: include/hw/misc/xlnx-zynq-ddrc.h
1151 F: include/hw/misc/zynq_slcr.h
1152 F: include/hw/adc/zynq-xadc.h
1153 X: hw/ssi/xilinx_*
hw/arm/Kconfig
+1
@@ -289,6 +289,7 @@ config ZYNQ
289 select XILINX_AXI
290 select XILINX_SPI
291 select XILINX_SPIPS
292 + select XLNX_ZYNQ_DDRC
293 select ZYNQ_DEVCFG
294
295 config ARM_V7M
hw/arm/xilinx_zynq.c
+13 -3
@@ -201,6 +201,17 @@ static void zynq_set_boot_mode(Object *obj, const char *str,
201 m->boot_mode = mode;
202 }
203
204 +static void ddr_ctrl_init(uint32_t base)
205 +{
206 + DeviceState *dev;
207 + SysBusDevice *busdev;
208 +
209 + dev = qdev_new("zynq.ddr-ctlr");
210 + busdev = SYS_BUS_DEVICE(dev);
211 + sysbus_realize_and_unref(busdev, &error_fatal);
212 + sysbus_mmio_map(busdev, 0, base);
213 +}
214 +
215 static void zynq_init(MachineState *machine)
216 {
217 ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine);
@@ -312,6 +323,8 @@ static void zynq_init(MachineState *machine)
323 sysbus_create_varargs("cadence_ttc", 0xF8002000,
324 pic[69-GIC_INTERNAL], pic[70-GIC_INTERNAL], pic[71-GIC_INTERNAL], NULL);
325
326 + ddr_ctrl_init(0xF8006000);
327 +
328 gem_init(0xE000B000, pic[54 - GIC_INTERNAL]);
329 gem_init(0xE000C000, pic[77 - GIC_INTERNAL]);
330
@@ -393,9 +406,6 @@ static void zynq_init(MachineState *machine)
406 /* System Watchdog Timer Registers */
407 create_unimplemented_device("zynq.swdt", 0xF8005000, 4 * KiB);
408
396 - /* DDR memory controller */
397 - create_unimplemented_device("zynq.ddrc", 0xF8006000, 4 * KiB);
398 -
409 /* AXI_HP Interface (AFI) */
410 create_unimplemented_device("zynq.axi_hp0", 0xF8008000, 0x28);
411 create_unimplemented_device("zynq.axi_hp1", 0xF8009000, 0x28);
hw/misc/Kconfig
+3
@@ -254,4 +254,7 @@ config IOSB
254 config XLNX_VERSAL_TRNG
255 bool
256
257 +config XLNX_ZYNQ_DDRC
258 + bool
259 +
260 source macio/Kconfig
hw/misc/meson.build
+1
@@ -97,6 +97,7 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
97 ))
98 system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
99 system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
100 +system_ss.add(when: 'CONFIG_XLNX_ZYNQ_DDRC', if_true: files('xlnx-zynq-ddrc.c'))
101 system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c'))
102 system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c'))
103 system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
hw/misc/xlnx-zynq-ddrc.c new
+421
@@ -0,0 +1,421 @@
1 +/*
2 + * QEMU model of the Xilinx Zynq Double Data Rate Controller
3 + *
4 + * Copyright (c) Beckhoff Automation GmbH. & Co. KG
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#include "qemu/osdep.h"
10 +#include "hw/core/sysbus.h"
11 +#include "hw/core/register.h"
12 +#include "qemu/bitops.h"
13 +#include "qemu/log.h"
14 +#include "qapi/error.h"
15 +#include "hw/core/registerfields.h"
16 +#include "system/block-backend.h"
17 +#include "system/address-spaces.h"
18 +#include "system/memory.h"
19 +#include "system/dma.h"
20 +#include "hw/misc/xlnx-zynq-ddrc.h"
21 +#include "migration/vmstate.h"
22 +
23 +#ifndef DDRCTRL_ERR_DEBUG
24 +#define DDRCTRL_ERR_DEBUG 0
25 +#endif
26 +
27 +static void zynq_ddrctrl_post_write(RegisterInfo *reg, uint64_t val)
28 +{
29 + DDRCTRLState *s = DDRCTRL(reg->opaque);
30 + if (reg->access->addr == A_DDRC_CTRL) {
31 + if (val & 0x1) {
32 + s->reg[R_MODE_STS_REG] |=
33 + (R_MODE_STS_REG_DDR_REG_OPERATING_MODE_MASK & 0x1);
34 + } else {
35 + s->reg[R_MODE_STS_REG] &=
36 + ~R_MODE_STS_REG_DDR_REG_OPERATING_MODE_MASK;
37 + }
38 + }
39 +}
40 +
41 +static const RegisterAccessInfo xlnx_zynq_ddrc_regs_info[] = {
42 + /* 0x00 - 0x3C: Basic DDRC control and config */
43 + { .name = "DDRC_CTRL",
44 + .addr = A_DDRC_CTRL,
45 + .reset = 0x00000200,
46 + .post_write = zynq_ddrctrl_post_write },
47 + { .name = "TWO_RANK_CFG",
48 + .addr = A_TWO_RANK_CFG,
49 + .reset = 0x000C1076 },
50 + { .name = "HPR_REG",
51 + .addr = A_HPR_REG,
52 + .reset = 0x03C0780F },
53 + { .name = "LPR_REG",
54 + .addr = A_LPR_REG,
55 + .reset = 0x03C0780F },
56 + { .name = "WR_REG",
57 + .addr = A_WR_REG,
58 + .reset = 0x0007F80F },
59 + { .name = "DRAM_PARAM_REG0",
60 + .addr = A_DRAM_PARAM_REG0,
61 + .reset = 0x00041016 },
62 + { .name = "DRAM_PARAM_REG1",
63 + .addr = A_DRAM_PARAM_REG1,
64 + .reset = 0x351B48D9 },
65 + { .name = "DRAM_PARAM_REG2",
66 + .addr = A_DRAM_PARAM_REG2,
67 + .reset = 0x83015904 },
68 + { .name = "DRAM_PARAM_REG3",
69 + .addr = A_DRAM_PARAM_REG3,
70 + .reset = 0x250882D0 },
71 + { .name = "DRAM_PARAM_REG4",
72 + .addr = A_DRAM_PARAM_REG4,
73 + .reset = 0x0000003C },
74 + { .name = "DRAM_INIT_PARAM",
75 + .addr = A_DRAM_INIT_PARAM,
76 + .reset = 0x00002007 },
77 + { .name = "DRAM_EMR_REG",
78 + .addr = A_DRAM_EMR_REG,
79 + .reset = 0x00000008 },
80 + { .name = "DRAM_EMR_MR_REG",
81 + .addr = A_DRAM_EMR_MR_REG,
82 + .reset = 0x00000940 },
83 + { .name = "DRAM_BURST8_RDWR",
84 + .addr = A_DRAM_BURST8_RDWR,
85 + .reset = 0x00020034 },
86 + { .name = "DRAM_DISABLE_DQ",
87 + .addr = A_DRAM_DISABLE_DQ },
88 + { .name = "DRAM_ADDR_MAP_BANK",
89 + .addr = A_DRAM_ADDR_MAP_BANK,
90 + .reset = 0x00000F77 },
91 + { .name = "DRAM_ADDR_MAP_COL",
92 + .addr = A_DRAM_ADDR_MAP_COL,
93 + .reset = 0xFFF00000 },
94 + { .name = "DRAM_ADDR_MAP_ROW",
95 + .addr = A_DRAM_ADDR_MAP_ROW,
96 + .reset = 0x0FF55555 },
97 + { .name = "DRAM_ODT_REG",
98 + .addr = A_DRAM_ODT_REG,
99 + .reset = 0x00000249 },
100 +
101 + /* 0x4C - 0x5C: PHY and DLL */
102 + { .name = "PHY_DBG_REG",
103 + .addr = A_PHY_DBG_REG },
104 + { .name = "PHY_CMD_TIMEOUT_RDDATA_CPT",
105 + .addr = A_PHY_CMD_TIMEOUT_RDDATA_CPT,
106 + .reset = 0x00010200 },
107 + { .name = "MODE_STS_REG",
108 + .addr = A_MODE_STS_REG },
109 + { .name = "DLL_CALIB",
110 + .addr = A_DLL_CALIB,
111 + .reset = 0x00000101 },
112 + { .name = "ODT_DELAY_HOLD",
113 + .addr = A_ODT_DELAY_HOLD,
114 + .reset = 0x00000023 },
115 +
116 + /* 0x60 - 0x7C: Control registers */
117 + { .name = "CTRL_REG1",
118 + .addr = A_CTRL_REG1,
119 + .reset = 0x0000003E },
120 + { .name = "CTRL_REG2",
121 + .addr = A_CTRL_REG2,
122 + .reset = 0x00020000 },
123 + { .name = "CTRL_REG3",
124 + .addr = A_CTRL_REG3,
125 + .reset = 0x00284027 },
126 + { .name = "CTRL_REG4",
127 + .addr = A_CTRL_REG4,
128 + .reset = 0x00001610 },
129 + { .name = "CTRL_REG5",
130 + .addr = A_CTRL_REG5,
131 + .reset = 0x00455111 },
132 + { .name = "CTRL_REG6",
133 + .addr = A_CTRL_REG6,
134 + .reset = 0x00032222 },
135 +
136 + /* 0xA0 - 0xB4: Refresh, ZQ, powerdown, misc */
137 + { .name = "CHE_REFRESH_TIMER0",
138 + .addr = A_CHE_REFRESH_TIMER0,
139 + .reset = 0x00008000 },
140 + { .name = "CHE_T_ZQ",
141 + .addr = A_CHE_T_ZQ,
142 + .reset = 0x10300802 },
143 + { .name = "CHE_T_ZQ_SHORT_INTERVAL_REG",
144 + .addr = A_CHE_T_ZQ_SHORT_INTERVAL_REG,
145 + .reset = 0x0020003A },
146 + { .name = "DEEP_PWRDWN_REG",
147 + .addr = A_DEEP_PWRDWN_REG },
148 + { .name = "REG_2C",
149 + .addr = A_REG_2C },
150 + { .name = "REG_2D",
151 + .addr = A_REG_2D,
152 + .reset = 0x00000200 },
153 +
154 + /* 0xB8 - 0xF8: ECC, DFI, etc. */
155 + { .name = "DFI_TIMING",
156 + .addr = A_DFI_TIMING,
157 + .reset = 0x00200067 },
158 + { .name = "CHE_ECC_CONTROL_REG_OFFSET",
159 + .addr = A_CHE_ECC_CONTROL_REG_OFFSET },
160 + { .name = "CHE_CORR_ECC_LOG_REG_OFFSET",
161 + .addr = A_CHE_CORR_ECC_LOG_REG_OFFSET },
162 + { .name = "CHE_CORR_ECC_ADDR_REG_OFFSET",
163 + .addr = A_CHE_CORR_ECC_ADDR_REG_OFFSET },
164 + { .name = "CHE_CORR_ECC_DATA_31_0_REG_OFFSET",
165 + .addr = A_CHE_CORR_ECC_DATA_31_0_REG_OFFSET },
166 + { .name = "CHE_CORR_ECC_DATA_63_32_REG_OFFSET",
167 + .addr = A_CHE_CORR_ECC_DATA_63_32_REG_OFFSET },
168 + { .name = "CHE_CORR_ECC_DATA_71_64_REG_OFFSET",
169 + .addr = A_CHE_CORR_ECC_DATA_71_64_REG_OFFSET },
170 + { .name = "CHE_UNCORR_ECC_LOG_REG_OFFSET",
171 + .addr = A_CHE_UNCORR_ECC_LOG_REG_OFFSET },
172 + { .name = "CHE_UNCORR_ECC_ADDR_REG_OFFSET",
173 + .addr = A_CHE_UNCORR_ECC_ADDR_REG_OFFSET },
174 + { .name = "CHE_UNCORR_ECC_DATA_31_0_REG_OFFSET",
175 + .addr = A_CHE_UNCORR_ECC_DATA_31_0_REG_OFFSET },
176 + { .name = "CHE_UNCORR_ECC_DATA_63_32_REG_OFFSET",
177 + .addr = A_CHE_UNCORR_ECC_DATA_63_32_REG_OFFSET },
178 + { .name = "CHE_UNCORR_ECC_DATA_71_64_REG_OFFSET",
179 + .addr = A_CHE_UNCORR_ECC_DATA_71_64_REG_OFFSET },
180 + { .name = "CHE_ECC_STATS_REG_OFFSET",
181 + .addr = A_CHE_ECC_STATS_REG_OFFSET },
182 + { .name = "ECC_SCRUB",
183 + .addr = A_ECC_SCRUB,
184 + .reset = 0x00000008 },
185 + { .name = "CHE_ECC_CORR_BIT_MASK_31_0_REG_OFFSET",
186 + .addr = A_CHE_ECC_CORR_BIT_MASK_31_0_REG_OFFSET },
187 + { .name = "CHE_ECC_CORR_BIT_MASK_63_32_REG_OFFSET",
188 + .addr = A_CHE_ECC_CORR_BIT_MASK_63_32_REG_OFFSET },
189 +
190 + /* 0x114 - 0x174: PHY config, ratios, DQS, WE */
191 + { .name = "PHY_RCVER_ENABLE",
192 + .addr = A_PHY_RCVER_ENABLE },
193 + { .name = "PHY_CONFIG0",
194 + .addr = A_PHY_CONFIG0,
195 + .reset = 0x40000001 },
196 + { .name = "PHY_CONFIG1",
197 + .addr = A_PHY_CONFIG1,
198 + .reset = 0x40000001 },
199 + { .name = "PHY_CONFIG2",
200 + .addr = A_PHY_CONFIG2,
201 + .reset = 0x40000001 },
202 + { .name = "PHY_CONFIG3",
203 + .addr = A_PHY_CONFIG3,
204 + .reset = 0x40000001 },
205 + { .name = "PHY_INIT_RATIO0",
206 + .addr = A_PHY_INIT_RATIO0 },
207 + { .name = "PHY_INIT_RATIO1",
208 + .addr = A_PHY_INIT_RATIO1 },
209 + { .name = "PHY_INIT_RATIO2",
210 + .addr = A_PHY_INIT_RATIO2 },
211 + { .name = "PHY_INIT_RATIO3",
212 + .addr = A_PHY_INIT_RATIO3 },
213 + { .name = "PHY_RD_DQS_CFG0",
214 + .addr = A_PHY_RD_DQS_CFG0,
215 + .reset = 0x00000040 },
216 + { .name = "PHY_RD_DQS_CFG1",
217 + .addr = A_PHY_RD_DQS_CFG1,
218 + .reset = 0x00000040 },
219 + { .name = "PHY_RD_DQS_CFG2",
220 + .addr = A_PHY_RD_DQS_CFG2,
221 + .reset = 0x00000040 },
222 + { .name = "PHY_RD_DQS_CFG3",
223 + .addr = A_PHY_RD_DQS_CFG3,
224 + .reset = 0x00000040 },
225 + { .name = "PHY_WR_DQS_CFG0",
226 + .addr = A_PHY_WR_DQS_CFG0 },
227 + { .name = "PHY_WR_DQS_CFG1",
228 + .addr = A_PHY_WR_DQS_CFG1 },
229 + { .name = "PHY_WR_DQS_CFG2",
230 + .addr = A_PHY_WR_DQS_CFG2 },
231 + { .name = "PHY_WR_DQS_CFG3",
232 + .addr = A_PHY_WR_DQS_CFG3 },
233 + { .name = "PHY_WE_CFG0",
234 + .addr = A_PHY_WE_CFG0,
235 + .reset = 0x00000040 },
236 + { .name = "PHY_WE_CFG1",
237 + .addr = A_PHY_WE_CFG1,
238 + .reset = 0x00000040 },
239 + { .name = "PHY_WE_CFG2",
240 + .addr = A_PHY_WE_CFG2,
241 + .reset = 0x00000040 },
242 + { .name = "PHY_WE_CFG3",
243 + .addr = A_PHY_WE_CFG3,
244 + .reset = 0x00000040 },
245 +
246 + /* 0x17C - 0x194: Write data slaves, misc */
247 + { .name = "WR_DATA_SLV0",
248 + .addr = A_WR_DATA_SLV0,
249 + .reset = 0x00000080 },
250 + { .name = "WR_DATA_SLV1",
251 + .addr = A_WR_DATA_SLV1,
252 + .reset = 0x00000080 },
253 + { .name = "WR_DATA_SLV2",
254 + .addr = A_WR_DATA_SLV2,
255 + .reset = 0x00000080 },
256 + { .name = "WR_DATA_SLV3",
257 + .addr = A_WR_DATA_SLV3,
258 + .reset = 0x00000080 },
259 + { .name = "REG_64",
260 + .addr = A_REG_64,
261 + .reset = 0x10020000 },
262 + { .name = "REG_65",
263 + .addr = A_REG_65 },
264 +
265 + /* 0x1A4 - 0x1C4: Misc registers */
266 + { .name = "REG69_6A0",
267 + .addr = A_REG69_6A0 },
268 + { .name = "REG69_6A1",
269 + .addr = A_REG69_6A1 },
270 + { .name = "REG6C_6D2",
271 + .addr = A_REG6C_6D2 },
272 + { .name = "REG6C_6D3",
273 + .addr = A_REG6C_6D3 },
274 + { .name = "REG6E_710",
275 + .addr = A_REG6E_710 },
276 + { .name = "REG6E_711",
277 + .addr = A_REG6E_711 },
278 + { .name = "REG6E_712",
279 + .addr = A_REG6E_712 },
280 + { .name = "REG6E_713",
281 + .addr = A_REG6E_713 },
282 +
283 + /* 0x1CC - 0x1E8: DLL, PHY status */
284 + { .name = "PHY_DLL_STS0",
285 + .addr = A_PHY_DLL_STS0 },
286 + { .name = "PHY_DLL_STS1",
287 + .addr = A_PHY_DLL_STS1 },
288 + { .name = "PHY_DLL_STS2",
289 + .addr = A_PHY_DLL_STS2 },
290 + { .name = "PHY_DLL_STS3",
291 + .addr = A_PHY_DLL_STS3 },
292 + { .name = "DLL_LOCK_STS",
293 + .addr = A_DLL_LOCK_STS },
294 + { .name = "PHY_CTRL_STS",
295 + .addr = A_PHY_CTRL_STS },
296 + { .name = "PHY_CTRL_STS_REG2",
297 + .addr = A_PHY_CTRL_STS_REG2 },
298 +
299 + /* 0x200 - 0x2B4: AXI, LPDDR, misc */
300 + { .name = "AXI_ID",
301 + .addr = A_AXI_ID },
302 + { .name = "PAGE_MASK",
303 + .addr = A_PAGE_MASK },
304 + { .name = "AXI_PRIORITY_WR_PORT0",
305 + .addr = A_AXI_PRIORITY_WR_PORT0,
306 + .reset = 0x000803FF },
307 + { .name = "AXI_PRIORITY_WR_PORT1",
308 + .addr = A_AXI_PRIORITY_WR_PORT1,
309 + .reset = 0x000803FF },
310 + { .name = "AXI_PRIORITY_WR_PORT2",
311 + .addr = A_AXI_PRIORITY_WR_PORT2,
312 + .reset = 0x000803FF },
313 + { .name = "AXI_PRIORITY_WR_PORT3",
314 + .addr = A_AXI_PRIORITY_WR_PORT3,
315 + .reset = 0x000803FF },
316 + { .name = "AXI_PRIORITY_RD_PORT0",
317 + .addr = A_AXI_PRIORITY_RD_PORT0,
318 + .reset = 0x000003FF },
319 + { .name = "AXI_PRIORITY_RD_PORT1",
320 + .addr = A_AXI_PRIORITY_RD_PORT1,
321 + .reset = 0x000003FF },
322 + { .name = "AXI_PRIORITY_RD_PORT2",
323 + .addr = A_AXI_PRIORITY_RD_PORT2,
324 + .reset = 0x000003FF },
325 + { .name = "AXI_PRIORITY_RD_PORT3",
326 + .addr = A_AXI_PRIORITY_RD_PORT3,
327 + .reset = 0x000003FF },
328 + { .name = "EXCL_ACCESS_CFG0",
329 + .addr = A_EXCL_ACCESS_CFG0 },
330 + { .name = "EXCL_ACCESS_CFG1",
331 + .addr = A_EXCL_ACCESS_CFG1 },
332 + { .name = "EXCL_ACCESS_CFG2",
333 + .addr = A_EXCL_ACCESS_CFG2 },
334 + { .name = "EXCL_ACCESS_CFG3",
335 + .addr = A_EXCL_ACCESS_CFG3 },
336 + { .name = "MODE_REG_READ",
337 + .addr = A_MODE_REG_READ },
338 + { .name = "LPDDR_CTRL0",
339 + .addr = A_LPDDR_CTRL0 },
340 + { .name = "LPDDR_CTRL1",
341 + .addr = A_LPDDR_CTRL1 },
342 + { .name = "LPDDR_CTRL2",
343 + .addr = A_LPDDR_CTRL2,
344 + .reset = 0x003C0015 },
345 + { .name = "LPDDR_CTRL3",
346 + .addr = A_LPDDR_CTRL3,
347 + .reset = 0x00000601 },
348 +};
349 +
350 +static void zynq_ddrctrl_reset(DeviceState *dev)
351 +{
352 + DDRCTRLState *s = DDRCTRL(dev);
353 + int i;
354 +
355 + for (i = 0; i < ZYNQ_DDRCTRL_NUM_REG; ++i) {
356 + register_reset(&s->regs_info[i]);
357 + }
358 +}
359 +
360 +static const MemoryRegionOps ddrctrl_ops = {
361 + .read = register_read_memory,
362 + .write = register_write_memory,
363 + .endianness = DEVICE_LITTLE_ENDIAN,
364 + .valid = {
365 + .min_access_size = 4,
366 + .max_access_size = 4,
367 + },
368 + .impl = {
369 + .min_access_size = 4,
370 + .max_access_size = 4,
371 + },
372 +};
373 +
374 +static const VMStateDescription vmstate_zynq_ddrctrl = {
375 + .name = "zynq_ddrc",
376 + .version_id = 1,
377 + .minimum_version_id = 1,
378 + .fields = (const VMStateField[]) {
379 + VMSTATE_UINT32_ARRAY(reg, DDRCTRLState, ZYNQ_DDRCTRL_NUM_REG),
380 + VMSTATE_END_OF_LIST()
381 + }
382 +};
383 +
384 +static void zynq_ddrctrl_init(Object *obj)
385 +{
386 + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
387 + DDRCTRLState *s = DDRCTRL(obj);
388 +
389 + s->reg_array =
390 + register_init_block32(DEVICE(obj), xlnx_zynq_ddrc_regs_info,
391 + ARRAY_SIZE(xlnx_zynq_ddrc_regs_info),
392 + s->regs_info, s->reg,
393 + &ddrctrl_ops,
394 + DDRCTRL_ERR_DEBUG,
395 + ZYNQ_DDRCTRL_MMIO_SIZE);
396 +
397 + sysbus_init_mmio(sbd, &s->reg_array->mem);
398 +}
399 +
400 +static void zynq_ddrctrl_class_init(ObjectClass *klass, const void *data)
401 +{
402 + DeviceClass *dc = DEVICE_CLASS(klass);
403 +
404 + device_class_set_legacy_reset(dc, zynq_ddrctrl_reset);
405 + dc->vmsd = &vmstate_zynq_ddrctrl;
406 +}
407 +
408 +static const TypeInfo ddrctrl_info = {
409 + .name = TYPE_DDRCTRL,
410 + .parent = TYPE_SYS_BUS_DEVICE,
411 + .instance_size = sizeof(DDRCTRLState),
412 + .instance_init = zynq_ddrctrl_init,
413 + .class_init = zynq_ddrctrl_class_init,
414 +};
415 +
416 +static void ddrctrl_register_types(void)
417 +{
418 + type_register_static(&ddrctrl_info);
419 +}
420 +
421 +type_init(ddrctrl_register_types)
include/hw/misc/xlnx-zynq-ddrc.h new
+147
@@ -0,0 +1,147 @@
1 +/*
2 + * QEMU model of the Xilinx Zynq Double Data Rate Controller
3 + *
4 + * Copyright (c) Beckhoff Automation GmbH. & Co. KG
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +
9 +#ifndef XLNX_ZYNQ_DDRC_H
10 +#define XLNX_ZYNQ_DDRC_H
11 +
12 +#include "hw/core/sysbus.h"
13 +#include "hw/core/register.h"
14 +
15 +#define TYPE_DDRCTRL "zynq.ddr-ctlr"
16 +OBJECT_DECLARE_SIMPLE_TYPE(DDRCTRLState, DDRCTRL)
17 +
18 +REG32(DDRC_CTRL, 0x00)
19 +REG32(TWO_RANK_CFG, 0x04)
20 +REG32(HPR_REG, 0x08)
21 +REG32(LPR_REG, 0x0C)
22 +REG32(WR_REG, 0x10)
23 +REG32(DRAM_PARAM_REG0, 0x14)
24 +REG32(DRAM_PARAM_REG1, 0x18)
25 +REG32(DRAM_PARAM_REG2, 0x1C)
26 +REG32(DRAM_PARAM_REG3, 0x20)
27 +REG32(DRAM_PARAM_REG4, 0x24)
28 +REG32(DRAM_INIT_PARAM, 0x28)
29 +REG32(DRAM_EMR_REG, 0x2C)
30 +REG32(DRAM_EMR_MR_REG, 0x30)
31 +REG32(DRAM_BURST8_RDWR, 0x34)
32 +REG32(DRAM_DISABLE_DQ, 0x38)
33 +REG32(DRAM_ADDR_MAP_BANK, 0x3C)
34 +REG32(DRAM_ADDR_MAP_COL, 0x40)
35 +REG32(DRAM_ADDR_MAP_ROW, 0x44)
36 +REG32(DRAM_ODT_REG, 0x48)
37 +REG32(PHY_DBG_REG, 0x4C)
38 +REG32(PHY_CMD_TIMEOUT_RDDATA_CPT, 0x50)
39 +REG32(MODE_STS_REG, 0x54)
40 + FIELD(MODE_STS_REG, DDR_REG_DBG_STALL, 3, 3)
41 + FIELD(MODE_STS_REG, DDR_REG_OPERATING_MODE, 0, 2)
42 +REG32(DLL_CALIB, 0x58)
43 +REG32(ODT_DELAY_HOLD, 0x5C)
44 +REG32(CTRL_REG1, 0x60)
45 +REG32(CTRL_REG2, 0x64)
46 +REG32(CTRL_REG3, 0x68)
47 +REG32(CTRL_REG4, 0x6C)
48 +REG32(CTRL_REG5, 0x78)
49 +REG32(CTRL_REG6, 0x7C)
50 +REG32(CHE_REFRESH_TIMER0, 0xA0)
51 +REG32(CHE_T_ZQ, 0xA4)
52 +REG32(CHE_T_ZQ_SHORT_INTERVAL_REG, 0xA8)
53 +REG32(DEEP_PWRDWN_REG, 0xAC)
54 +REG32(REG_2C, 0xB0)
55 +REG32(REG_2D, 0xB4)
56 +REG32(DFI_TIMING, 0xB8)
57 +REG32(CHE_ECC_CONTROL_REG_OFFSET, 0xC4)
58 +REG32(CHE_CORR_ECC_LOG_REG_OFFSET, 0xC8)
59 +REG32(CHE_CORR_ECC_ADDR_REG_OFFSET, 0xCC)
60 +REG32(CHE_CORR_ECC_DATA_31_0_REG_OFFSET, 0xD0)
61 +REG32(CHE_CORR_ECC_DATA_63_32_REG_OFFSET, 0xD4)
62 +REG32(CHE_CORR_ECC_DATA_71_64_REG_OFFSET, 0xD8)
63 +REG32(CHE_UNCORR_ECC_LOG_REG_OFFSET, 0xDC)
64 +REG32(CHE_UNCORR_ECC_ADDR_REG_OFFSET, 0xE0)
65 +REG32(CHE_UNCORR_ECC_DATA_31_0_REG_OFFSET, 0xE4)
66 +REG32(CHE_UNCORR_ECC_DATA_63_32_REG_OFFSET, 0xE8)
67 +REG32(CHE_UNCORR_ECC_DATA_71_64_REG_OFFSET, 0xEC)
68 +REG32(CHE_ECC_STATS_REG_OFFSET, 0xF0)
69 +REG32(ECC_SCRUB, 0xF4)
70 +REG32(CHE_ECC_CORR_BIT_MASK_31_0_REG_OFFSET, 0xF8)
71 +REG32(CHE_ECC_CORR_BIT_MASK_63_32_REG_OFFSET, 0xFC)
72 +REG32(PHY_RCVER_ENABLE, 0x114)
73 +REG32(PHY_CONFIG0, 0x118)
74 +REG32(PHY_CONFIG1, 0x11C)
75 +REG32(PHY_CONFIG2, 0x120)
76 +REG32(PHY_CONFIG3, 0x124)
77 +REG32(PHY_INIT_RATIO0, 0x12C)
78 +REG32(PHY_INIT_RATIO1, 0x130)
79 +REG32(PHY_INIT_RATIO2, 0x134)
80 +REG32(PHY_INIT_RATIO3, 0x138)
81 +REG32(PHY_RD_DQS_CFG0, 0x140)
82 +REG32(PHY_RD_DQS_CFG1, 0x144)
83 +REG32(PHY_RD_DQS_CFG2, 0x148)
84 +REG32(PHY_RD_DQS_CFG3, 0x14C)
85 +REG32(PHY_WR_DQS_CFG0, 0x154)
86 +REG32(PHY_WR_DQS_CFG1, 0x158)
87 +REG32(PHY_WR_DQS_CFG2, 0x15C)
88 +REG32(PHY_WR_DQS_CFG3, 0x160)
89 +REG32(PHY_WE_CFG0, 0x168)
90 +REG32(PHY_WE_CFG1, 0x16C)
91 +REG32(PHY_WE_CFG2, 0x170)
92 +REG32(PHY_WE_CFG3, 0x174)
93 +REG32(WR_DATA_SLV0, 0x17C)
94 +REG32(WR_DATA_SLV1, 0x180)
95 +REG32(WR_DATA_SLV2, 0x184)
96 +REG32(WR_DATA_SLV3, 0x188)
97 +REG32(REG_64, 0x190)
98 +REG32(REG_65, 0x194)
99 +REG32(REG69_6A0, 0x1A4)
100 +REG32(REG69_6A1, 0x1A8)
101 +REG32(REG6C_6D2, 0x1B0)
102 +REG32(REG6C_6D3, 0x1B4)
103 +REG32(REG6E_710, 0x1B8)
104 +REG32(REG6E_711, 0x1BC)
105 +REG32(REG6E_712, 0x1C0)
106 +REG32(REG6E_713, 0x1C4)
107 +REG32(PHY_DLL_STS0, 0x1CC)
108 +REG32(PHY_DLL_STS1, 0x1D0)
109 +REG32(PHY_DLL_STS2, 0x1D4)
110 +REG32(PHY_DLL_STS3, 0x1D8)
111 +REG32(DLL_LOCK_STS, 0x1E0)
112 +REG32(PHY_CTRL_STS, 0x1E4)
113 +REG32(PHY_CTRL_STS_REG2, 0x1E8)
114 +REG32(AXI_ID, 0x200)
115 +REG32(PAGE_MASK, 0x204)
116 +REG32(AXI_PRIORITY_WR_PORT0, 0x208)
117 +REG32(AXI_PRIORITY_WR_PORT1, 0x20C)
118 +REG32(AXI_PRIORITY_WR_PORT2, 0x210)
119 +REG32(AXI_PRIORITY_WR_PORT3, 0x214)
120 +REG32(AXI_PRIORITY_RD_PORT0, 0x218)
121 +REG32(AXI_PRIORITY_RD_PORT1, 0x21C)
122 +REG32(AXI_PRIORITY_RD_PORT2, 0x220)
123 +REG32(AXI_PRIORITY_RD_PORT3, 0x224)
124 +REG32(EXCL_ACCESS_CFG0, 0x294)
125 +REG32(EXCL_ACCESS_CFG1, 0x298)
126 +REG32(EXCL_ACCESS_CFG2, 0x29C)
127 +REG32(EXCL_ACCESS_CFG3, 0x2A0)
128 +REG32(MODE_REG_READ, 0x2A4)
129 +REG32(LPDDR_CTRL0, 0x2A8)
130 +REG32(LPDDR_CTRL1, 0x2AC)
131 +REG32(LPDDR_CTRL2, 0x2B0)
132 +REG32(LPDDR_CTRL3, 0x2B4)
133 +
134 +
135 +#define ZYNQ_DDRCTRL_MMIO_SIZE 0x400
136 +#define ZYNQ_DDRCTRL_NUM_REG (ZYNQ_DDRCTRL_MMIO_SIZE / 4)
137 +
138 +typedef struct DDRCTRLState {
139 + SysBusDevice parent_obj;
140 +
141 + MemoryRegion iomem;
142 +
143 + RegisterInfoArray *reg_array;
144 + uint32_t reg[ZYNQ_DDRCTRL_NUM_REG];
145 + RegisterInfo regs_info[ZYNQ_DDRCTRL_NUM_REG];
146 +} DDRCTRLState;
147 +#endif