]>
Commit | Line | Data |
---|---|---|
461a6a6f TH |
1 | /* |
2 | * QEMU model of the Xilinx BBRAM Battery Backed RAM | |
3 | * | |
4 | * Copyright (c) 2014-2021 Xilinx Inc. | |
213bf5c1 | 5 | * Copyright (c) 2023 Advanced Micro Devices, Inc. |
461a6a6f TH |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
25 | ||
26 | #include "qemu/osdep.h" | |
27 | #include "hw/nvram/xlnx-bbram.h" | |
28 | ||
29 | #include "qemu/error-report.h" | |
30 | #include "qemu/log.h" | |
31 | #include "qapi/error.h" | |
32 | #include "sysemu/blockdev.h" | |
33 | #include "migration/vmstate.h" | |
34 | #include "hw/qdev-properties.h" | |
35 | #include "hw/qdev-properties-system.h" | |
36 | #include "hw/nvram/xlnx-efuse.h" | |
37 | ||
38 | #ifndef XLNX_BBRAM_ERR_DEBUG | |
39 | #define XLNX_BBRAM_ERR_DEBUG 0 | |
40 | #endif | |
41 | ||
42 | REG32(BBRAM_STATUS, 0x0) | |
43 | FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) | |
44 | FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) | |
45 | FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) | |
46 | FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) | |
47 | REG32(BBRAM_CTRL, 0x4) | |
48 | FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) | |
49 | REG32(PGM_MODE, 0x8) | |
50 | REG32(BBRAM_AES_CRC, 0xc) | |
51 | REG32(BBRAM_0, 0x10) | |
52 | REG32(BBRAM_1, 0x14) | |
53 | REG32(BBRAM_2, 0x18) | |
54 | REG32(BBRAM_3, 0x1c) | |
55 | REG32(BBRAM_4, 0x20) | |
56 | REG32(BBRAM_5, 0x24) | |
57 | REG32(BBRAM_6, 0x28) | |
58 | REG32(BBRAM_7, 0x2c) | |
59 | REG32(BBRAM_8, 0x30) | |
60 | REG32(BBRAM_SLVERR, 0x34) | |
61 | FIELD(BBRAM_SLVERR, ENABLE, 0, 1) | |
62 | REG32(BBRAM_ISR, 0x38) | |
63 | FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) | |
64 | REG32(BBRAM_IMR, 0x3c) | |
65 | FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) | |
66 | REG32(BBRAM_IER, 0x40) | |
67 | FIELD(BBRAM_IER, APB_SLVERR, 0, 1) | |
68 | REG32(BBRAM_IDR, 0x44) | |
69 | FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) | |
70 | REG32(BBRAM_MSW_LOCK, 0x4c) | |
71 | FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) | |
72 | ||
73 | #define R_MAX (R_BBRAM_MSW_LOCK + 1) | |
74 | ||
75 | #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) | |
76 | ||
77 | #define BBRAM_PGM_MAGIC 0x757bdf0d | |
78 | ||
79 | QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs)); | |
80 | ||
81 | static bool bbram_msw_locked(XlnxBBRam *s) | |
82 | { | |
83 | return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; | |
84 | } | |
85 | ||
86 | static bool bbram_pgm_enabled(XlnxBBRam *s) | |
87 | { | |
88 | return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; | |
89 | } | |
90 | ||
91 | static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail) | |
92 | { | |
2e9ce532 | 93 | Error *errp = NULL; |
461a6a6f TH |
94 | |
95 | error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", | |
96 | blk_name(s->blk), detail); | |
97 | error_report("%s", error_get_pretty(errp)); | |
98 | error_free(errp); | |
99 | ||
100 | g_free(detail); | |
101 | } | |
102 | ||
103 | static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) | |
104 | { | |
105 | uint32_t *ram = &s->regs[R_BBRAM_0]; | |
106 | int nr = RAM_MAX; | |
107 | ||
108 | if (!s->blk) { | |
109 | return; | |
110 | } | |
111 | ||
112 | s->blk_ro = !blk_supports_write_perm(s->blk); | |
113 | if (!s->blk_ro) { | |
114 | int rc; | |
115 | ||
116 | rc = blk_set_perm(s->blk, | |
117 | (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), | |
118 | BLK_PERM_ALL, NULL); | |
119 | if (rc) { | |
120 | s->blk_ro = true; | |
121 | } | |
122 | } | |
123 | if (s->blk_ro) { | |
124 | warn_report("%s: Skip saving updates to read-only BBRAM backstore.", | |
125 | blk_name(s->blk)); | |
126 | } | |
127 | ||
a9262f55 | 128 | if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { |
461a6a6f TH |
129 | error_setg(errp, |
130 | "%s: Failed to read %u bytes from BBRAM backstore.", | |
131 | blk_name(s->blk), nr); | |
132 | return; | |
133 | } | |
134 | ||
135 | /* Convert from little-endian backstore for each 32-bit word */ | |
136 | nr /= 4; | |
137 | while (nr--) { | |
138 | ram[nr] = le32_to_cpu(ram[nr]); | |
139 | } | |
140 | } | |
141 | ||
142 | static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) | |
143 | { | |
144 | uint32_t le32; | |
145 | unsigned offset; | |
146 | int rc; | |
147 | ||
148 | assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); | |
149 | ||
150 | /* Backstore is always in little-endian */ | |
151 | le32 = cpu_to_le32(s->regs[hwaddr / 4]); | |
152 | ||
153 | /* Update zeroized flag */ | |
154 | if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { | |
155 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); | |
156 | } | |
157 | ||
158 | if (!s->blk || s->blk_ro) { | |
159 | return; | |
160 | } | |
161 | ||
162 | offset = hwaddr - A_BBRAM_0; | |
a9262f55 | 163 | rc = blk_pwrite(s->blk, offset, 4, &le32, 0); |
461a6a6f TH |
164 | if (rc < 0) { |
165 | bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); | |
166 | } | |
167 | } | |
168 | ||
169 | static void bbram_bdrv_zero(XlnxBBRam *s) | |
170 | { | |
171 | int rc; | |
172 | ||
173 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); | |
174 | ||
175 | if (!s->blk || s->blk_ro) { | |
176 | return; | |
177 | } | |
178 | ||
179 | rc = blk_make_zero(s->blk, 0); | |
180 | if (rc < 0) { | |
181 | bbram_bdrv_error(s, rc, g_strdup("zeroizing")); | |
182 | } | |
183 | ||
184 | /* Restore bbram8 if it is non-zero */ | |
185 | if (s->regs[R_BBRAM_8]) { | |
186 | bbram_bdrv_sync(s, A_BBRAM_8); | |
187 | } | |
188 | } | |
189 | ||
190 | static void bbram_zeroize(XlnxBBRam *s) | |
191 | { | |
192 | int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ | |
193 | ||
194 | memset(&s->regs[R_BBRAM_0], 0, nr); | |
195 | bbram_bdrv_zero(s); | |
196 | } | |
197 | ||
198 | static void bbram_update_irq(XlnxBBRam *s) | |
199 | { | |
200 | bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; | |
201 | ||
202 | qemu_set_irq(s->irq_bbram, pending); | |
203 | } | |
204 | ||
205 | static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) | |
206 | { | |
207 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
208 | uint32_t val = val64; | |
209 | ||
210 | if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { | |
211 | bbram_zeroize(s); | |
212 | /* The bit is self clearing */ | |
213 | s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; | |
214 | } | |
215 | } | |
216 | ||
217 | static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) | |
218 | { | |
219 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
220 | uint32_t val = val64; | |
221 | ||
222 | if (val == BBRAM_PGM_MAGIC) { | |
223 | bbram_zeroize(s); | |
224 | ||
225 | /* The status bit is cleared only by POR */ | |
226 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); | |
227 | } | |
228 | } | |
229 | ||
230 | static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) | |
231 | { | |
232 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
233 | uint32_t calc_crc; | |
234 | ||
235 | if (!bbram_pgm_enabled(s)) { | |
236 | /* We are not in programming mode, don't do anything */ | |
237 | return; | |
238 | } | |
239 | ||
240 | /* Perform the AES integrity check */ | |
241 | s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; | |
242 | ||
243 | /* | |
244 | * Set check status. | |
245 | * | |
246 | * ZynqMP BBRAM check has a zero-u32 prepended; see: | |
247 | * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311 | |
248 | */ | |
249 | calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0], | |
250 | (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads); | |
251 | ||
252 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, | |
253 | (s->regs[R_BBRAM_AES_CRC] == calc_crc)); | |
254 | } | |
255 | ||
256 | static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) | |
257 | { | |
258 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
259 | uint32_t original_data = *(uint32_t *) reg->data; | |
260 | ||
261 | if (bbram_pgm_enabled(s)) { | |
262 | return val64; | |
263 | } else { | |
264 | /* We are not in programming mode, don't do anything */ | |
265 | qemu_log_mask(LOG_GUEST_ERROR, | |
266 | "Not in programming mode, dropping the write\n"); | |
267 | return original_data; | |
268 | } | |
269 | } | |
270 | ||
271 | static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) | |
272 | { | |
273 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
274 | ||
275 | bbram_bdrv_sync(s, reg->access->addr); | |
276 | } | |
277 | ||
278 | static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) | |
279 | { | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) | |
284 | { | |
285 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
286 | ||
287 | return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; | |
288 | } | |
289 | ||
290 | static bool bbram_r8_readonly(XlnxBBRam *s) | |
291 | { | |
292 | return !bbram_pgm_enabled(s) || bbram_msw_locked(s); | |
293 | } | |
294 | ||
295 | static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) | |
296 | { | |
297 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
298 | ||
299 | if (bbram_r8_readonly(s)) { | |
300 | val64 = *(uint32_t *)reg->data; | |
301 | } | |
302 | ||
303 | return val64; | |
304 | } | |
305 | ||
306 | static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) | |
307 | { | |
308 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
309 | ||
310 | if (!bbram_r8_readonly(s)) { | |
311 | bbram_bdrv_sync(s, A_BBRAM_8); | |
312 | } | |
313 | } | |
314 | ||
315 | static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) | |
316 | { | |
317 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
318 | ||
319 | /* Never lock if bbram8 is wo; and, only POR can clear the lock */ | |
320 | if (s->bbram8_wo) { | |
321 | val64 = 0; | |
322 | } else { | |
323 | val64 |= s->regs[R_BBRAM_MSW_LOCK]; | |
324 | } | |
325 | ||
326 | return val64; | |
327 | } | |
328 | ||
329 | static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) | |
330 | { | |
331 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
332 | ||
333 | bbram_update_irq(s); | |
334 | } | |
335 | ||
336 | static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) | |
337 | { | |
338 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
339 | uint32_t val = val64; | |
340 | ||
341 | s->regs[R_BBRAM_IMR] &= ~val; | |
342 | bbram_update_irq(s); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) | |
347 | { | |
348 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); | |
349 | uint32_t val = val64; | |
350 | ||
351 | s->regs[R_BBRAM_IMR] |= val; | |
352 | bbram_update_irq(s); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | static RegisterAccessInfo bbram_ctrl_regs_info[] = { | |
357 | { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, | |
358 | .rsvd = 0xee, | |
359 | .ro = 0x3ff, | |
360 | },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, | |
361 | .post_write = bbram_ctrl_postw, | |
362 | },{ .name = "PGM_MODE", .addr = A_PGM_MODE, | |
363 | .post_write = bbram_pgm_mode_postw, | |
364 | },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, | |
365 | .post_write = bbram_aes_crc_postw, | |
366 | .post_read = bbram_wo_postr, | |
367 | },{ .name = "BBRAM_0", .addr = A_BBRAM_0, | |
368 | .pre_write = bbram_key_prew, | |
369 | .post_write = bbram_key_postw, | |
370 | .post_read = bbram_wo_postr, | |
371 | },{ .name = "BBRAM_1", .addr = A_BBRAM_1, | |
372 | .pre_write = bbram_key_prew, | |
373 | .post_write = bbram_key_postw, | |
374 | .post_read = bbram_wo_postr, | |
375 | },{ .name = "BBRAM_2", .addr = A_BBRAM_2, | |
376 | .pre_write = bbram_key_prew, | |
377 | .post_write = bbram_key_postw, | |
378 | .post_read = bbram_wo_postr, | |
379 | },{ .name = "BBRAM_3", .addr = A_BBRAM_3, | |
380 | .pre_write = bbram_key_prew, | |
381 | .post_write = bbram_key_postw, | |
382 | .post_read = bbram_wo_postr, | |
383 | },{ .name = "BBRAM_4", .addr = A_BBRAM_4, | |
384 | .pre_write = bbram_key_prew, | |
385 | .post_write = bbram_key_postw, | |
386 | .post_read = bbram_wo_postr, | |
387 | },{ .name = "BBRAM_5", .addr = A_BBRAM_5, | |
388 | .pre_write = bbram_key_prew, | |
389 | .post_write = bbram_key_postw, | |
390 | .post_read = bbram_wo_postr, | |
391 | },{ .name = "BBRAM_6", .addr = A_BBRAM_6, | |
392 | .pre_write = bbram_key_prew, | |
393 | .post_write = bbram_key_postw, | |
394 | .post_read = bbram_wo_postr, | |
395 | },{ .name = "BBRAM_7", .addr = A_BBRAM_7, | |
396 | .pre_write = bbram_key_prew, | |
397 | .post_write = bbram_key_postw, | |
398 | .post_read = bbram_wo_postr, | |
399 | },{ .name = "BBRAM_8", .addr = A_BBRAM_8, | |
400 | .pre_write = bbram_r8_prew, | |
401 | .post_write = bbram_r8_postw, | |
402 | .post_read = bbram_r8_postr, | |
403 | },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, | |
404 | .rsvd = ~1, | |
405 | },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, | |
406 | .w1c = 0x1, | |
407 | .post_write = bbram_isr_postw, | |
408 | },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, | |
409 | .ro = 0x1, | |
410 | },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, | |
411 | .pre_write = bbram_ier_prew, | |
412 | },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, | |
413 | .pre_write = bbram_idr_prew, | |
414 | },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, | |
415 | .pre_write = bbram_msw_lock_prew, | |
416 | .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, | |
417 | } | |
418 | }; | |
419 | ||
213bf5c1 | 420 | static void bbram_ctrl_reset_hold(Object *obj) |
461a6a6f | 421 | { |
213bf5c1 | 422 | XlnxBBRam *s = XLNX_BBRAM(obj); |
461a6a6f TH |
423 | unsigned int i; |
424 | ||
425 | for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { | |
426 | if (i < R_BBRAM_0 || i > R_BBRAM_8) { | |
427 | register_reset(&s->regs_info[i]); | |
428 | } | |
429 | } | |
430 | ||
431 | bbram_update_irq(s); | |
432 | } | |
433 | ||
434 | static const MemoryRegionOps bbram_ctrl_ops = { | |
435 | .read = register_read_memory, | |
436 | .write = register_write_memory, | |
437 | .endianness = DEVICE_LITTLE_ENDIAN, | |
438 | .valid = { | |
439 | .min_access_size = 4, | |
440 | .max_access_size = 4, | |
441 | }, | |
442 | }; | |
443 | ||
444 | static void bbram_ctrl_realize(DeviceState *dev, Error **errp) | |
445 | { | |
446 | XlnxBBRam *s = XLNX_BBRAM(dev); | |
447 | ||
448 | if (s->crc_zpads) { | |
449 | s->bbram8_wo = true; | |
450 | } | |
451 | ||
452 | bbram_bdrv_read(s, errp); | |
453 | } | |
454 | ||
455 | static void bbram_ctrl_init(Object *obj) | |
456 | { | |
457 | XlnxBBRam *s = XLNX_BBRAM(obj); | |
458 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
459 | RegisterInfoArray *reg_array; | |
460 | ||
461 | reg_array = | |
462 | register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, | |
463 | ARRAY_SIZE(bbram_ctrl_regs_info), | |
464 | s->regs_info, s->regs, | |
465 | &bbram_ctrl_ops, | |
466 | XLNX_BBRAM_ERR_DEBUG, | |
467 | R_MAX * 4); | |
468 | ||
469 | sysbus_init_mmio(sbd, ®_array->mem); | |
470 | sysbus_init_irq(sbd, &s->irq_bbram); | |
471 | } | |
472 | ||
473 | static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, | |
474 | void *opaque, Error **errp) | |
475 | { | |
476 | DeviceState *dev = DEVICE(obj); | |
477 | ||
478 | qdev_prop_drive.set(obj, v, name, opaque, errp); | |
479 | ||
480 | /* Fill initial data if backend is attached after realized */ | |
481 | if (dev->realized) { | |
482 | bbram_bdrv_read(XLNX_BBRAM(obj), errp); | |
483 | } | |
484 | } | |
485 | ||
486 | static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name, | |
487 | void *opaque, Error **errp) | |
488 | { | |
489 | qdev_prop_drive.get(obj, v, name, opaque, errp); | |
490 | } | |
491 | ||
492 | static void bbram_prop_release_drive(Object *obj, const char *name, | |
493 | void *opaque) | |
494 | { | |
495 | qdev_prop_drive.release(obj, name, opaque); | |
496 | } | |
497 | ||
498 | static const PropertyInfo bbram_prop_drive = { | |
499 | .name = "str", | |
500 | .description = "Node name or ID of a block device to use as BBRAM backend", | |
501 | .realized_set_allowed = true, | |
502 | .get = bbram_prop_get_drive, | |
503 | .set = bbram_prop_set_drive, | |
504 | .release = bbram_prop_release_drive, | |
505 | }; | |
506 | ||
507 | static const VMStateDescription vmstate_bbram_ctrl = { | |
508 | .name = TYPE_XLNX_BBRAM, | |
509 | .version_id = 1, | |
510 | .minimum_version_id = 1, | |
18d10e61 | 511 | .fields = (const VMStateField[]) { |
461a6a6f TH |
512 | VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), |
513 | VMSTATE_END_OF_LIST(), | |
514 | } | |
515 | }; | |
516 | ||
517 | static Property bbram_ctrl_props[] = { | |
518 | DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), | |
519 | DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), | |
520 | DEFINE_PROP_END_OF_LIST(), | |
521 | }; | |
522 | ||
523 | static void bbram_ctrl_class_init(ObjectClass *klass, void *data) | |
524 | { | |
525 | DeviceClass *dc = DEVICE_CLASS(klass); | |
213bf5c1 | 526 | ResettableClass *rc = RESETTABLE_CLASS(klass); |
461a6a6f | 527 | |
213bf5c1 | 528 | rc->phases.hold = bbram_ctrl_reset_hold; |
461a6a6f TH |
529 | dc->realize = bbram_ctrl_realize; |
530 | dc->vmsd = &vmstate_bbram_ctrl; | |
531 | device_class_set_props(dc, bbram_ctrl_props); | |
532 | } | |
533 | ||
534 | static const TypeInfo bbram_ctrl_info = { | |
535 | .name = TYPE_XLNX_BBRAM, | |
536 | .parent = TYPE_SYS_BUS_DEVICE, | |
537 | .instance_size = sizeof(XlnxBBRam), | |
538 | .class_init = bbram_ctrl_class_init, | |
539 | .instance_init = bbram_ctrl_init, | |
540 | }; | |
541 | ||
542 | static void bbram_ctrl_register_types(void) | |
543 | { | |
544 | type_register_static(&bbram_ctrl_info); | |
545 | } | |
546 | ||
547 | type_init(bbram_ctrl_register_types) |