master
c 421 lines 12.6 KB
Raw
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)