1 /* 2 * QEMU model of the CFU Configuration Unit. 3 * 4 * Copyright (C) 2023, Advanced Micro Devices, Inc. 5 * 6 * Written by Edgar E. Iglesias <edgar.iglesias@gmail.com>, 7 * Sai Pavan Boddu <sai.pavan.boddu@amd.com>, 8 * Francisco Iglesias <francisco.iglesias@amd.com> 9 * 10 * SPDX-License-Identifier: GPL-2.0-or-later 11 */ 12 13 #include "qemu/osdep.h" 14 #include "hw/sysbus.h" 15 #include "hw/register.h" 16 #include "hw/irq.h" 17 #include "qemu/bitops.h" 18 #include "qemu/log.h" 19 #include "qemu/units.h" 20 #include "migration/vmstate.h" 21 #include "hw/qdev-properties.h" 22 #include "hw/qdev-properties-system.h" 23 #include "hw/misc/xlnx-versal-cfu.h" 24 25 #ifndef XLNX_VERSAL_CFU_APB_ERR_DEBUG 26 #define XLNX_VERSAL_CFU_APB_ERR_DEBUG 0 27 #endif 28 29 #define KEYHOLE_STREAM_4K (4 * KiB) 30 #define KEYHOLE_STREAM_256K (256 * KiB) 31 #define CFRAME_BROADCAST_ROW 0x1F 32 33 bool update_wfifo(hwaddr addr, uint64_t value, 34 uint32_t *wfifo, uint32_t *wfifo_ret) 35 { 36 unsigned int idx = extract32(addr, 2, 2); 37 38 wfifo[idx] = value; 39 40 if (idx == 3) { 41 memcpy(wfifo_ret, wfifo, WFIFO_SZ * sizeof(uint32_t)); 42 memset(wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); 43 return true; 44 } 45 46 return false; 47 } 48 49 static void cfu_imr_update_irq(XlnxVersalCFUAPB *s) 50 { 51 bool pending = s->regs[R_CFU_ISR] & ~s->regs[R_CFU_IMR]; 52 qemu_set_irq(s->irq_cfu_imr, pending); 53 } 54 55 static void cfu_isr_postw(RegisterInfo *reg, uint64_t val64) 56 { 57 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); 58 cfu_imr_update_irq(s); 59 } 60 61 static uint64_t cfu_ier_prew(RegisterInfo *reg, uint64_t val64) 62 { 63 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); 64 uint32_t val = val64; 65 66 s->regs[R_CFU_IMR] &= ~val; 67 cfu_imr_update_irq(s); 68 return 0; 69 } 70 71 static uint64_t cfu_idr_prew(RegisterInfo *reg, uint64_t val64) 72 { 73 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); 74 uint32_t val = val64; 75 76 s->regs[R_CFU_IMR] |= val; 77 cfu_imr_update_irq(s); 78 return 0; 79 } 80 81 static uint64_t cfu_itr_prew(RegisterInfo *reg, uint64_t val64) 82 { 83 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); 84 uint32_t val = val64; 85 86 s->regs[R_CFU_ISR] |= val; 87 cfu_imr_update_irq(s); 88 return 0; 89 } 90 91 static void cfu_fgcr_postw(RegisterInfo *reg, uint64_t val64) 92 { 93 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); 94 uint32_t val = (uint32_t)val64; 95 96 /* Do a scan. It always looks good. */ 97 if (FIELD_EX32(val, CFU_FGCR, SC_HBC_TRIGGER)) { 98 ARRAY_FIELD_DP32(s->regs, CFU_STATUS, SCAN_CLEAR_PASS, 1); 99 ARRAY_FIELD_DP32(s->regs, CFU_STATUS, SCAN_CLEAR_DONE, 1); 100 } 101 } 102 103 static const RegisterAccessInfo cfu_apb_regs_info[] = { 104 { .name = "CFU_ISR", .addr = A_CFU_ISR, 105 .rsvd = 0xfffffc00, 106 .w1c = 0x3ff, 107 .post_write = cfu_isr_postw, 108 },{ .name = "CFU_IMR", .addr = A_CFU_IMR, 109 .reset = 0x3ff, 110 .rsvd = 0xfffffc00, 111 .ro = 0x3ff, 112 },{ .name = "CFU_IER", .addr = A_CFU_IER, 113 .rsvd = 0xfffffc00, 114 .pre_write = cfu_ier_prew, 115 },{ .name = "CFU_IDR", .addr = A_CFU_IDR, 116 .rsvd = 0xfffffc00, 117 .pre_write = cfu_idr_prew, 118 },{ .name = "CFU_ITR", .addr = A_CFU_ITR, 119 .rsvd = 0xfffffc00, 120 .pre_write = cfu_itr_prew, 121 },{ .name = "CFU_PROTECT", .addr = A_CFU_PROTECT, 122 .reset = 0x1, 123 },{ .name = "CFU_FGCR", .addr = A_CFU_FGCR, 124 .rsvd = 0xffff8000, 125 .post_write = cfu_fgcr_postw, 126 },{ .name = "CFU_CTL", .addr = A_CFU_CTL, 127 .rsvd = 0xffff0000, 128 },{ .name = "CFU_CRAM_RW", .addr = A_CFU_CRAM_RW, 129 .reset = 0x401f7d9, 130 .rsvd = 0xf8000000, 131 },{ .name = "CFU_MASK", .addr = A_CFU_MASK, 132 },{ .name = "CFU_CRC_EXPECT", .addr = A_CFU_CRC_EXPECT, 133 },{ .name = "CFU_CFRAME_LEFT_T0", .addr = A_CFU_CFRAME_LEFT_T0, 134 .rsvd = 0xfff00000, 135 },{ .name = "CFU_CFRAME_LEFT_T1", .addr = A_CFU_CFRAME_LEFT_T1, 136 .rsvd = 0xfff00000, 137 },{ .name = "CFU_CFRAME_LEFT_T2", .addr = A_CFU_CFRAME_LEFT_T2, 138 .rsvd = 0xfff00000, 139 },{ .name = "CFU_ROW_RANGE", .addr = A_CFU_ROW_RANGE, 140 .rsvd = 0xffffffc0, 141 .ro = 0x3f, 142 },{ .name = "CFU_STATUS", .addr = A_CFU_STATUS, 143 .rsvd = 0x80000000, 144 .ro = 0x7fffffff, 145 },{ .name = "CFU_INTERNAL_STATUS", .addr = A_CFU_INTERNAL_STATUS, 146 .rsvd = 0xff800000, 147 .ro = 0x7fffff, 148 },{ .name = "CFU_QWORD_CNT", .addr = A_CFU_QWORD_CNT, 149 .ro = 0xffffffff, 150 },{ .name = "CFU_CRC_LIVE", .addr = A_CFU_CRC_LIVE, 151 .ro = 0xffffffff, 152 },{ .name = "CFU_PENDING_READ_CNT", .addr = A_CFU_PENDING_READ_CNT, 153 .rsvd = 0xfe000000, 154 .ro = 0x1ffffff, 155 },{ .name = "CFU_FDRI_CNT", .addr = A_CFU_FDRI_CNT, 156 .ro = 0xffffffff, 157 },{ .name = "CFU_ECO1", .addr = A_CFU_ECO1, 158 },{ .name = "CFU_ECO2", .addr = A_CFU_ECO2, 159 } 160 }; 161 162 static void cfu_apb_reset(DeviceState *dev) 163 { 164 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(dev); 165 unsigned int i; 166 167 for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { 168 register_reset(&s->regs_info[i]); 169 } 170 memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); 171 172 s->regs[R_CFU_STATUS] |= R_CFU_STATUS_HC_COMPLETE_MASK; 173 cfu_imr_update_irq(s); 174 } 175 176 static const MemoryRegionOps cfu_apb_ops = { 177 .read = register_read_memory, 178 .write = register_write_memory, 179 .endianness = DEVICE_LITTLE_ENDIAN, 180 .valid = { 181 .min_access_size = 4, 182 .max_access_size = 4, 183 }, 184 }; 185 186 static void cfu_transfer_cfi_packet(XlnxVersalCFUAPB *s, uint8_t row_addr, 187 XlnxCfiPacket *pkt) 188 { 189 if (row_addr == CFRAME_BROADCAST_ROW) { 190 for (int i = 0; i < ARRAY_SIZE(s->cfg.cframe); i++) { 191 if (s->cfg.cframe[i]) { 192 xlnx_cfi_transfer_packet(s->cfg.cframe[i], pkt); 193 } 194 } 195 } else { 196 assert(row_addr < ARRAY_SIZE(s->cfg.cframe)); 197 198 if (s->cfg.cframe[row_addr]) { 199 xlnx_cfi_transfer_packet(s->cfg.cframe[row_addr], pkt); 200 } 201 } 202 } 203 204 static uint64_t cfu_stream_read(void *opaque, hwaddr addr, unsigned size) 205 { 206 qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" 207 HWADDR_PRIx "\n", __func__, addr); 208 return 0; 209 } 210 211 static void cfu_stream_write(void *opaque, hwaddr addr, uint64_t value, 212 unsigned size) 213 { 214 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(opaque); 215 uint32_t wfifo[WFIFO_SZ]; 216 217 if (update_wfifo(addr, value, s->wfifo, wfifo)) { 218 uint8_t packet_type, row_addr, reg_addr; 219 220 packet_type = extract32(wfifo[0], 24, 8); 221 row_addr = extract32(wfifo[0], 16, 5); 222 reg_addr = extract32(wfifo[0], 8, 6); 223 224 /* Compressed bitstreams are not supported yet. */ 225 if (ARRAY_FIELD_EX32(s->regs, CFU_CTL, DECOMPRESS) == 0) { 226 if (s->regs[R_CFU_FDRI_CNT]) { 227 XlnxCfiPacket pkt = { 228 .reg_addr = CFRAME_FDRI, 229 .data[0] = wfifo[0], 230 .data[1] = wfifo[1], 231 .data[2] = wfifo[2], 232 .data[3] = wfifo[3] 233 }; 234 235 cfu_transfer_cfi_packet(s, s->fdri_row_addr, &pkt); 236 237 s->regs[R_CFU_FDRI_CNT]--; 238 239 } else if (packet_type == PACKET_TYPE_CFU && 240 reg_addr == CFRAME_FDRI) { 241 242 /* Load R_CFU_FDRI_CNT, must be multiple of 25 */ 243 s->regs[R_CFU_FDRI_CNT] = wfifo[1]; 244 245 /* Store target row_addr */ 246 s->fdri_row_addr = row_addr; 247 248 if (wfifo[1] % 25 != 0) { 249 qemu_log_mask(LOG_GUEST_ERROR, 250 "CFU FDRI_CNT is not loaded with " 251 "a multiple of 25 value\n"); 252 } 253 254 } else if (packet_type == PACKET_TYPE_CFRAME) { 255 XlnxCfiPacket pkt = { 256 .reg_addr = reg_addr, 257 .data[0] = wfifo[1], 258 .data[1] = wfifo[2], 259 .data[2] = wfifo[3], 260 }; 261 cfu_transfer_cfi_packet(s, row_addr, &pkt); 262 } 263 } 264 } 265 } 266 267 static uint64_t cfu_sfr_read(void *opaque, hwaddr addr, unsigned size) 268 { 269 qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" 270 HWADDR_PRIx "\n", __func__, addr); 271 return 0; 272 } 273 274 static void cfu_sfr_write(void *opaque, hwaddr addr, uint64_t value, 275 unsigned size) 276 { 277 XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(opaque); 278 uint32_t wfifo[WFIFO_SZ]; 279 280 if (update_wfifo(addr, value, s->wfifo, wfifo)) { 281 uint8_t row_addr = extract32(wfifo[0], 23, 5); 282 uint32_t frame_addr = extract32(wfifo[0], 0, 23); 283 XlnxCfiPacket pkt = { .reg_addr = CFRAME_SFR, 284 .data[0] = frame_addr }; 285 286 if (s->cfg.cfu) { 287 cfu_transfer_cfi_packet(s->cfg.cfu, row_addr, &pkt); 288 } 289 } 290 } 291 292 static uint64_t cfu_fdro_read(void *opaque, hwaddr addr, unsigned size) 293 { 294 XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(opaque); 295 uint64_t ret = 0; 296 297 if (!fifo32_is_empty(&s->fdro_data)) { 298 ret = fifo32_pop(&s->fdro_data); 299 } 300 301 return ret; 302 } 303 304 static void cfu_fdro_write(void *opaque, hwaddr addr, uint64_t value, 305 unsigned size) 306 { 307 qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported write from addr=%" 308 HWADDR_PRIx "\n", __func__, addr); 309 } 310 311 static const MemoryRegionOps cfu_stream_ops = { 312 .read = cfu_stream_read, 313 .write = cfu_stream_write, 314 .endianness = DEVICE_LITTLE_ENDIAN, 315 .valid = { 316 .min_access_size = 4, 317 .max_access_size = 8, 318 }, 319 }; 320 321 static const MemoryRegionOps cfu_sfr_ops = { 322 .read = cfu_sfr_read, 323 .write = cfu_sfr_write, 324 .endianness = DEVICE_LITTLE_ENDIAN, 325 .valid = { 326 .min_access_size = 4, 327 .max_access_size = 4, 328 }, 329 }; 330 331 static const MemoryRegionOps cfu_fdro_ops = { 332 .read = cfu_fdro_read, 333 .write = cfu_fdro_write, 334 .endianness = DEVICE_LITTLE_ENDIAN, 335 .valid = { 336 .min_access_size = 4, 337 .max_access_size = 4, 338 }, 339 }; 340 341 static void cfu_apb_init(Object *obj) 342 { 343 XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(obj); 344 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 345 RegisterInfoArray *reg_array; 346 unsigned int i; 347 char *name; 348 349 memory_region_init(&s->iomem, obj, TYPE_XLNX_VERSAL_CFU_APB, R_MAX * 4); 350 reg_array = 351 register_init_block32(DEVICE(obj), cfu_apb_regs_info, 352 ARRAY_SIZE(cfu_apb_regs_info), 353 s->regs_info, s->regs, 354 &cfu_apb_ops, 355 XLNX_VERSAL_CFU_APB_ERR_DEBUG, 356 R_MAX * 4); 357 memory_region_add_subregion(&s->iomem, 358 0x0, 359 ®_array->mem); 360 sysbus_init_mmio(sbd, &s->iomem); 361 for (i = 0; i < NUM_STREAM; i++) { 362 name = g_strdup_printf(TYPE_XLNX_VERSAL_CFU_APB "-stream%d", i); 363 memory_region_init_io(&s->iomem_stream[i], obj, &cfu_stream_ops, s, 364 name, i == 0 ? KEYHOLE_STREAM_4K : 365 KEYHOLE_STREAM_256K); 366 sysbus_init_mmio(sbd, &s->iomem_stream[i]); 367 g_free(name); 368 } 369 sysbus_init_irq(sbd, &s->irq_cfu_imr); 370 } 371 372 static void cfu_sfr_init(Object *obj) 373 { 374 XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(obj); 375 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 376 377 memory_region_init_io(&s->iomem_sfr, obj, &cfu_sfr_ops, s, 378 TYPE_XLNX_VERSAL_CFU_SFR, KEYHOLE_STREAM_4K); 379 sysbus_init_mmio(sbd, &s->iomem_sfr); 380 } 381 382 static void cfu_sfr_reset_enter(Object *obj, ResetType type) 383 { 384 XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(obj); 385 386 memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); 387 } 388 389 static void cfu_fdro_init(Object *obj) 390 { 391 XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); 392 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 393 394 memory_region_init_io(&s->iomem_fdro, obj, &cfu_fdro_ops, s, 395 TYPE_XLNX_VERSAL_CFU_FDRO, KEYHOLE_STREAM_4K); 396 sysbus_init_mmio(sbd, &s->iomem_fdro); 397 fifo32_create(&s->fdro_data, 8 * KiB / sizeof(uint32_t)); 398 } 399 400 static void cfu_fdro_finalize(Object *obj) 401 { 402 XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); 403 404 fifo32_destroy(&s->fdro_data); 405 } 406 407 static void cfu_fdro_reset_enter(Object *obj, ResetType type) 408 { 409 XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); 410 411 fifo32_reset(&s->fdro_data); 412 } 413 414 static void cfu_fdro_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt) 415 { 416 XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(cfi_if); 417 418 if (fifo32_num_free(&s->fdro_data) >= ARRAY_SIZE(pkt->data)) { 419 for (int i = 0; i < ARRAY_SIZE(pkt->data); i++) { 420 fifo32_push(&s->fdro_data, pkt->data[i]); 421 } 422 } else { 423 /* It is a programming error to fill the fifo. */ 424 qemu_log_mask(LOG_GUEST_ERROR, 425 "CFU_FDRO: CFI data dropped due to full read fifo\n"); 426 } 427 } 428 429 static Property cfu_props[] = { 430 DEFINE_PROP_LINK("cframe0", XlnxVersalCFUAPB, cfg.cframe[0], 431 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 432 DEFINE_PROP_LINK("cframe1", XlnxVersalCFUAPB, cfg.cframe[1], 433 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 434 DEFINE_PROP_LINK("cframe2", XlnxVersalCFUAPB, cfg.cframe[2], 435 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 436 DEFINE_PROP_LINK("cframe3", XlnxVersalCFUAPB, cfg.cframe[3], 437 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 438 DEFINE_PROP_LINK("cframe4", XlnxVersalCFUAPB, cfg.cframe[4], 439 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 440 DEFINE_PROP_LINK("cframe5", XlnxVersalCFUAPB, cfg.cframe[5], 441 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 442 DEFINE_PROP_LINK("cframe6", XlnxVersalCFUAPB, cfg.cframe[6], 443 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 444 DEFINE_PROP_LINK("cframe7", XlnxVersalCFUAPB, cfg.cframe[7], 445 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 446 DEFINE_PROP_LINK("cframe8", XlnxVersalCFUAPB, cfg.cframe[8], 447 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 448 DEFINE_PROP_LINK("cframe9", XlnxVersalCFUAPB, cfg.cframe[9], 449 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 450 DEFINE_PROP_LINK("cframe10", XlnxVersalCFUAPB, cfg.cframe[10], 451 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 452 DEFINE_PROP_LINK("cframe11", XlnxVersalCFUAPB, cfg.cframe[11], 453 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 454 DEFINE_PROP_LINK("cframe12", XlnxVersalCFUAPB, cfg.cframe[12], 455 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 456 DEFINE_PROP_LINK("cframe13", XlnxVersalCFUAPB, cfg.cframe[13], 457 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 458 DEFINE_PROP_LINK("cframe14", XlnxVersalCFUAPB, cfg.cframe[14], 459 TYPE_XLNX_CFI_IF, XlnxCfiIf *), 460 DEFINE_PROP_END_OF_LIST(), 461 }; 462 463 static Property cfu_sfr_props[] = { 464 DEFINE_PROP_LINK("cfu", XlnxVersalCFUSFR, cfg.cfu, 465 TYPE_XLNX_VERSAL_CFU_APB, XlnxVersalCFUAPB *), 466 DEFINE_PROP_END_OF_LIST(), 467 }; 468 469 static const VMStateDescription vmstate_cfu_apb = { 470 .name = TYPE_XLNX_VERSAL_CFU_APB, 471 .version_id = 1, 472 .minimum_version_id = 1, 473 .fields = (const VMStateField[]) { 474 VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUAPB, 4), 475 VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFUAPB, R_MAX), 476 VMSTATE_UINT8(fdri_row_addr, XlnxVersalCFUAPB), 477 VMSTATE_END_OF_LIST(), 478 } 479 }; 480 481 static const VMStateDescription vmstate_cfu_fdro = { 482 .name = TYPE_XLNX_VERSAL_CFU_FDRO, 483 .version_id = 1, 484 .minimum_version_id = 1, 485 .fields = (const VMStateField[]) { 486 VMSTATE_FIFO32(fdro_data, XlnxVersalCFUFDRO), 487 VMSTATE_END_OF_LIST(), 488 } 489 }; 490 491 static const VMStateDescription vmstate_cfu_sfr = { 492 .name = TYPE_XLNX_VERSAL_CFU_SFR, 493 .version_id = 1, 494 .minimum_version_id = 1, 495 .fields = (const VMStateField[]) { 496 VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUSFR, 4), 497 VMSTATE_END_OF_LIST(), 498 } 499 }; 500 501 static void cfu_apb_class_init(ObjectClass *klass, void *data) 502 { 503 DeviceClass *dc = DEVICE_CLASS(klass); 504 505 dc->reset = cfu_apb_reset; 506 dc->vmsd = &vmstate_cfu_apb; 507 device_class_set_props(dc, cfu_props); 508 } 509 510 static void cfu_fdro_class_init(ObjectClass *klass, void *data) 511 { 512 DeviceClass *dc = DEVICE_CLASS(klass); 513 ResettableClass *rc = RESETTABLE_CLASS(klass); 514 XlnxCfiIfClass *xcic = XLNX_CFI_IF_CLASS(klass); 515 516 dc->vmsd = &vmstate_cfu_fdro; 517 xcic->cfi_transfer_packet = cfu_fdro_cfi_transfer_packet; 518 rc->phases.enter = cfu_fdro_reset_enter; 519 } 520 521 static void cfu_sfr_class_init(ObjectClass *klass, void *data) 522 { 523 DeviceClass *dc = DEVICE_CLASS(klass); 524 ResettableClass *rc = RESETTABLE_CLASS(klass); 525 526 device_class_set_props(dc, cfu_sfr_props); 527 dc->vmsd = &vmstate_cfu_sfr; 528 rc->phases.enter = cfu_sfr_reset_enter; 529 } 530 531 static const TypeInfo cfu_apb_info = { 532 .name = TYPE_XLNX_VERSAL_CFU_APB, 533 .parent = TYPE_SYS_BUS_DEVICE, 534 .instance_size = sizeof(XlnxVersalCFUAPB), 535 .class_init = cfu_apb_class_init, 536 .instance_init = cfu_apb_init, 537 .interfaces = (InterfaceInfo[]) { 538 { TYPE_XLNX_CFI_IF }, 539 { } 540 } 541 }; 542 543 static const TypeInfo cfu_fdro_info = { 544 .name = TYPE_XLNX_VERSAL_CFU_FDRO, 545 .parent = TYPE_SYS_BUS_DEVICE, 546 .instance_size = sizeof(XlnxVersalCFUFDRO), 547 .class_init = cfu_fdro_class_init, 548 .instance_init = cfu_fdro_init, 549 .instance_finalize = cfu_fdro_finalize, 550 .interfaces = (InterfaceInfo[]) { 551 { TYPE_XLNX_CFI_IF }, 552 { } 553 } 554 }; 555 556 static const TypeInfo cfu_sfr_info = { 557 .name = TYPE_XLNX_VERSAL_CFU_SFR, 558 .parent = TYPE_SYS_BUS_DEVICE, 559 .instance_size = sizeof(XlnxVersalCFUSFR), 560 .class_init = cfu_sfr_class_init, 561 .instance_init = cfu_sfr_init, 562 }; 563 564 static void cfu_apb_register_types(void) 565 { 566 type_register_static(&cfu_apb_info); 567 type_register_static(&cfu_fdro_info); 568 type_register_static(&cfu_sfr_info); 569 } 570 571 type_init(cfu_apb_register_types) 572