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