| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU model of the Configuration Frame Control module | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2023, Advanced Micro Devices, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Written by Francisco Iglesias <francisco.iglesias@amd.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: GPL-2.0-or-later | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "hw/sysbus.h"
 | 
					
						
							|  |  |  | #include "hw/register.h"
 | 
					
						
							|  |  |  | #include "hw/registerfields.h"
 | 
					
						
							|  |  |  | #include "qemu/bitops.h"
 | 
					
						
							|  |  |  | #include "qemu/log.h"
 | 
					
						
							|  |  |  | #include "qemu/units.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							|  |  |  | #include "hw/qdev-properties.h"
 | 
					
						
							|  |  |  | #include "migration/vmstate.h"
 | 
					
						
							|  |  |  | #include "hw/irq.h"
 | 
					
						
							|  |  |  | #include "hw/misc/xlnx-versal-cframe-reg.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef XLNX_VERSAL_CFRAME_REG_ERR_DEBUG
 | 
					
						
							|  |  |  | #define XLNX_VERSAL_CFRAME_REG_ERR_DEBUG 0
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define KEYHOLE_STREAM_4K (4 * KiB)
 | 
					
						
							|  |  |  | #define N_WORDS_128BIT 4
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_BLOCKTYPE 6
 | 
					
						
							|  |  |  | #define MAX_BLOCKTYPE_FRAMES 0xFFFFF
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  |     CFRAME_CMD_WCFG = 1, | 
					
						
							|  |  |  |     CFRAME_CMD_ROWON = 2, | 
					
						
							|  |  |  |     CFRAME_CMD_ROWOFF = 3, | 
					
						
							|  |  |  |     CFRAME_CMD_RCFG = 4, | 
					
						
							|  |  |  |     CFRAME_CMD_DLPARK = 5, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     guint ua = GPOINTER_TO_UINT(a); | 
					
						
							|  |  |  |     guint ub = GPOINTER_TO_UINT(b); | 
					
						
							|  |  |  |     return (ua > ub) - (ua < ub); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_imr_update_irq(XlnxVersalCFrameReg *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool pending = s->regs[R_CFRM_ISR0] & ~s->regs[R_CFRM_IMR0]; | 
					
						
							|  |  |  |     qemu_set_irq(s->irq_cfrm_imr, pending); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_isr_postw(RegisterInfo *reg, uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  |     cfrm_imr_update_irq(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cfrm_ier_prew(RegisterInfo *reg, uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->regs[R_CFRM_IMR0] &= ~s->regs[R_CFRM_IER0]; | 
					
						
							|  |  |  |     s->regs[R_CFRM_IER0] = 0; | 
					
						
							|  |  |  |     cfrm_imr_update_irq(s); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cfrm_idr_prew(RegisterInfo *reg, uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->regs[R_CFRM_IMR0] |= s->regs[R_CFRM_IDR0]; | 
					
						
							|  |  |  |     s->regs[R_CFRM_IDR0] = 0; | 
					
						
							|  |  |  |     cfrm_imr_update_irq(s); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cfrm_itr_prew(RegisterInfo *reg, uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->regs[R_CFRM_ISR0] |= s->regs[R_CFRM_ITR0]; | 
					
						
							|  |  |  |     s->regs[R_CFRM_ITR0] = 0; | 
					
						
							|  |  |  |     cfrm_imr_update_irq(s); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_incr_far(XlnxVersalCFrameReg *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint32_t faddr = ARRAY_FIELD_EX32(s->regs, FAR0, FRAME_ADDR); | 
					
						
							|  |  |  |     uint32_t blktype = ARRAY_FIELD_EX32(s->regs, FAR0, BLOCKTYPE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(blktype <= MAX_BLOCKTYPE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     faddr++; | 
					
						
							|  |  |  |     if (faddr > s->cfg.blktype_num_frames[blktype]) { | 
					
						
							|  |  |  |         /* Restart from 0 and increment block type */ | 
					
						
							|  |  |  |         faddr = 0; | 
					
						
							|  |  |  |         blktype++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         assert(blktype <= MAX_BLOCKTYPE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ARRAY_FIELD_DP32(s->regs, FAR0, BLOCKTYPE, blktype); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ARRAY_FIELD_DP32(s->regs, FAR0, FRAME_ADDR, faddr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_fdri_post_write(RegisterInfo *reg, uint64_t val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->row_configured && s->rowon && s->wcfg) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (fifo32_num_free(&s->new_f_data) >= N_WORDS_128BIT) { | 
					
						
							|  |  |  |             fifo32_push(&s->new_f_data, s->regs[R_FDRI0]); | 
					
						
							|  |  |  |             fifo32_push(&s->new_f_data, s->regs[R_FDRI1]); | 
					
						
							|  |  |  |             fifo32_push(&s->new_f_data, s->regs[R_FDRI2]); | 
					
						
							|  |  |  |             fifo32_push(&s->new_f_data, s->regs[R_FDRI3]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (fifo32_is_full(&s->new_f_data)) { | 
					
						
							|  |  |  |             uint32_t addr = extract32(s->regs[R_FAR0], 0, 23); | 
					
						
							|  |  |  |             XlnxCFrame *f = g_new(XlnxCFrame, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (int i = 0; i < FRAME_NUM_WORDS; i++) { | 
					
						
							|  |  |  |                 f->data[i] = fifo32_pop(&s->new_f_data); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             g_tree_replace(s->cframes, GUINT_TO_POINTER(addr), f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             cframe_incr_far(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             fifo32_reset(&s->new_f_data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_readout_frames(XlnxVersalCFrameReg *s, uint32_t start_addr, | 
					
						
							|  |  |  |                                 uint32_t end_addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * NB: when our minimum glib version is at least 2.68 we can improve the | 
					
						
							|  |  |  |      * performance of the cframe traversal by using g_tree_lookup_node and | 
					
						
							|  |  |  |      * g_tree_node_next (instead of calling g_tree_lookup for finding each | 
					
						
							|  |  |  |      * cframe). | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     for (uint32_t addr = start_addr; addr < end_addr; addr++) { | 
					
						
							|  |  |  |         XlnxCFrame *f = g_tree_lookup(s->cframes, GUINT_TO_POINTER(addr)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Transmit the data if a frame was found */ | 
					
						
							|  |  |  |         if (f) { | 
					
						
							|  |  |  |             for (int i = 0; i < FRAME_NUM_WORDS; i += 4) { | 
					
						
							|  |  |  |                 XlnxCfiPacket pkt = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 pkt.data[0] = f->data[i]; | 
					
						
							|  |  |  |                 pkt.data[1] = f->data[i + 1]; | 
					
						
							|  |  |  |                 pkt.data[2] = f->data[i + 2]; | 
					
						
							|  |  |  |                 pkt.data[3] = f->data[i + 3]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (s->cfg.cfu_fdro) { | 
					
						
							|  |  |  |                     xlnx_cfi_transfer_packet(s->cfg.cfu_fdro, &pkt); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_frcnt_post_write(RegisterInfo *reg, uint64_t val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->row_configured && s->rowon && s->rcfg) { | 
					
						
							|  |  |  |         uint32_t start_addr = extract32(s->regs[R_FAR0], 0, 23); | 
					
						
							|  |  |  |         uint32_t end_addr = start_addr + s->regs[R_FRCNT0] / FRAME_NUM_QWORDS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cfrm_readout_frames(s, start_addr, end_addr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_cmd_post_write(RegisterInfo *reg, uint64_t val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->row_configured) { | 
					
						
							|  |  |  |         uint8_t cmd = ARRAY_FIELD_EX32(s->regs, CMD0, CMD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch (cmd) { | 
					
						
							|  |  |  |         case CFRAME_CMD_WCFG: | 
					
						
							|  |  |  |             s->wcfg = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CFRAME_CMD_ROWON: | 
					
						
							|  |  |  |             s->rowon = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CFRAME_CMD_ROWOFF: | 
					
						
							|  |  |  |             s->rowon = false; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CFRAME_CMD_RCFG: | 
					
						
							|  |  |  |             s->rcfg = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CFRAME_CMD_DLPARK: | 
					
						
							|  |  |  |             s->wcfg = false; | 
					
						
							|  |  |  |             s->rcfg = false; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cfrm_last_frame_bot_post_read(RegisterInfo *reg, | 
					
						
							|  |  |  |                                               uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  |     uint64_t val = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg->access->addr) { | 
					
						
							|  |  |  |     case A_LAST_FRAME_BOT0: | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT0, BLOCKTYPE1_LAST_FRAME_LSB, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[1]); | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT0, BLOCKTYPE0_LAST_FRAME, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[0]); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case A_LAST_FRAME_BOT1: | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE3_LAST_FRAME_LSB, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[3]); | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE2_LAST_FRAME, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[2]); | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE1_LAST_FRAME_MSB, | 
					
						
							|  |  |  |                          (s->cfg.blktype_num_frames[1] >> 12)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case A_LAST_FRAME_BOT2: | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_BOT2, BLOCKTYPE3_LAST_FRAME_MSB, | 
					
						
							|  |  |  |                          (s->cfg.blktype_num_frames[3] >> 4)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case A_LAST_FRAME_BOT3: | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cfrm_last_frame_top_post_read(RegisterInfo *reg, | 
					
						
							|  |  |  |                                               uint64_t val64) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  |     uint64_t val = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg->access->addr) { | 
					
						
							|  |  |  |     case A_LAST_FRAME_TOP0: | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_TOP0, BLOCKTYPE5_LAST_FRAME_LSB, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[5]); | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_TOP0, BLOCKTYPE4_LAST_FRAME, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[4]); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case A_LAST_FRAME_TOP1: | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_TOP1, BLOCKTYPE6_LAST_FRAME, | 
					
						
							|  |  |  |                          s->cfg.blktype_num_frames[6]); | 
					
						
							|  |  |  |         val = FIELD_DP32(val, LAST_FRAME_TOP1, BLOCKTYPE5_LAST_FRAME_MSB, | 
					
						
							|  |  |  |                          (s->cfg.blktype_num_frames[5] >> 12)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case A_LAST_FRAME_TOP2: | 
					
						
							|  |  |  |     case A_LAST_FRAME_BOT3: | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cfrm_far_sfr_post_write(RegisterInfo *reg, uint64_t val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->row_configured && s->rowon && s->rcfg) { | 
					
						
							|  |  |  |         uint32_t start_addr = extract32(s->regs[R_FAR_SFR0], 0, 23); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Readback 1 frame */ | 
					
						
							|  |  |  |         cfrm_readout_frames(s, start_addr, start_addr + 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const RegisterAccessInfo cframe_reg_regs_info[] = { | 
					
						
							|  |  |  |     {   .name = "CRC0",  .addr = A_CRC0, | 
					
						
							|  |  |  |         .rsvd = 0x00000000, | 
					
						
							|  |  |  |     },{ .name = "CRC1",  .addr = A_CRC0, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CRC2",  .addr = A_CRC0, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CRC3",  .addr = A_CRC0, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR0",  .addr = A_FAR0, | 
					
						
							|  |  |  |         .rsvd = 0xfe000000, | 
					
						
							|  |  |  |     },{ .name = "FAR1",  .addr = A_FAR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR2",  .addr = A_FAR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR3",  .addr = A_FAR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR_SFR0",  .addr = A_FAR_SFR0, | 
					
						
							|  |  |  |         .rsvd = 0xff800000, | 
					
						
							|  |  |  |     },{ .name = "FAR_SFR1",  .addr = A_FAR_SFR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR_SFR2",  .addr = A_FAR_SFR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FAR_SFR3",  .addr = A_FAR_SFR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .post_write = cfrm_far_sfr_post_write, | 
					
						
							|  |  |  |     },{ .name = "FDRI0",  .addr = A_FDRI0, | 
					
						
							|  |  |  |     },{ .name = "FDRI1",  .addr = A_FDRI1, | 
					
						
							|  |  |  |     },{ .name = "FDRI2",  .addr = A_FDRI2, | 
					
						
							|  |  |  |     },{ .name = "FDRI3",  .addr = A_FDRI3, | 
					
						
							|  |  |  |         .post_write = cfrm_fdri_post_write, | 
					
						
							|  |  |  |     },{ .name = "FRCNT0",  .addr = A_FRCNT0, | 
					
						
							|  |  |  |         .rsvd = 0x00000000, | 
					
						
							|  |  |  |     },{ .name = "FRCNT1",  .addr = A_FRCNT1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FRCNT2",  .addr = A_FRCNT2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "FRCNT3",  .addr = A_FRCNT3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .post_write = cfrm_frcnt_post_write | 
					
						
							|  |  |  |     },{ .name = "CMD0",  .addr = A_CMD0, | 
					
						
							|  |  |  |         .rsvd = 0xffffffe0, | 
					
						
							|  |  |  |     },{ .name = "CMD1",  .addr = A_CMD1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CMD2",  .addr = A_CMD2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CMD3",  .addr = A_CMD3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .post_write = cfrm_cmd_post_write | 
					
						
							|  |  |  |     },{ .name = "CR_MASK0",  .addr = A_CR_MASK0, | 
					
						
							|  |  |  |         .rsvd = 0x00000000, | 
					
						
							|  |  |  |     },{ .name = "CR_MASK1",  .addr = A_CR_MASK1, | 
					
						
							|  |  |  |         .rsvd = 0x00000000, | 
					
						
							|  |  |  |     },{ .name = "CR_MASK2",  .addr = A_CR_MASK2, | 
					
						
							|  |  |  |         .rsvd = 0x00000000, | 
					
						
							|  |  |  |     },{ .name = "CR_MASK3",  .addr = A_CR_MASK3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CTL0",  .addr = A_CTL0, | 
					
						
							|  |  |  |         .rsvd = 0xfffffff8, | 
					
						
							|  |  |  |     },{ .name = "CTL1",  .addr = A_CTL1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CTL2",  .addr = A_CTL2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CTL3",  .addr = A_CTL3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ISR0",  .addr = A_CFRM_ISR0, | 
					
						
							|  |  |  |         .rsvd = 0xffc04000, | 
					
						
							|  |  |  |         .w1c = 0x3bfff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ISR1",  .addr = A_CFRM_ISR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ISR2",  .addr = A_CFRM_ISR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ISR3",  .addr = A_CFRM_ISR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .post_write = cfrm_isr_postw, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IMR0",  .addr = A_CFRM_IMR0, | 
					
						
							|  |  |  |         .rsvd = 0xffc04000, | 
					
						
							|  |  |  |         .ro = 0xfffff, | 
					
						
							|  |  |  |         .reset = 0x3bfff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IMR1",  .addr = A_CFRM_IMR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IMR2",  .addr = A_CFRM_IMR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IMR3",  .addr = A_CFRM_IMR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IER0",  .addr = A_CFRM_IER0, | 
					
						
							|  |  |  |         .rsvd = 0xffc04000, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IER1",  .addr = A_CFRM_IER1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IER2",  .addr = A_CFRM_IER2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IER3",  .addr = A_CFRM_IER3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .pre_write = cfrm_ier_prew, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IDR0",  .addr = A_CFRM_IDR0, | 
					
						
							|  |  |  |         .rsvd = 0xffc04000, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IDR1",  .addr = A_CFRM_IDR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IDR2",  .addr = A_CFRM_IDR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_IDR3",  .addr = A_CFRM_IDR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .pre_write = cfrm_idr_prew, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ITR0",  .addr = A_CFRM_ITR0, | 
					
						
							|  |  |  |         .rsvd = 0xffc04000, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ITR1",  .addr = A_CFRM_ITR1, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ITR2",  .addr = A_CFRM_ITR2, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |     },{ .name = "CFRM_ITR3",  .addr = A_CFRM_ITR3, | 
					
						
							|  |  |  |         .rsvd = 0xffffffff, | 
					
						
							|  |  |  |         .pre_write = cfrm_itr_prew, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM00",  .addr = A_SEU_SYNDRM00, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM01",  .addr = A_SEU_SYNDRM01, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM02",  .addr = A_SEU_SYNDRM02, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM03",  .addr = A_SEU_SYNDRM03, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM10",  .addr = A_SEU_SYNDRM10, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM11",  .addr = A_SEU_SYNDRM11, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM12",  .addr = A_SEU_SYNDRM12, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM13",  .addr = A_SEU_SYNDRM13, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM20",  .addr = A_SEU_SYNDRM20, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM21",  .addr = A_SEU_SYNDRM21, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM22",  .addr = A_SEU_SYNDRM22, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM23",  .addr = A_SEU_SYNDRM23, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM30",  .addr = A_SEU_SYNDRM30, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM31",  .addr = A_SEU_SYNDRM31, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM32",  .addr = A_SEU_SYNDRM32, | 
					
						
							|  |  |  |     },{ .name = "SEU_SYNDRM33",  .addr = A_SEU_SYNDRM33, | 
					
						
							|  |  |  |     },{ .name = "SEU_VIRTUAL_SYNDRM0",  .addr = A_SEU_VIRTUAL_SYNDRM0, | 
					
						
							|  |  |  |     },{ .name = "SEU_VIRTUAL_SYNDRM1",  .addr = A_SEU_VIRTUAL_SYNDRM1, | 
					
						
							|  |  |  |     },{ .name = "SEU_VIRTUAL_SYNDRM2",  .addr = A_SEU_VIRTUAL_SYNDRM2, | 
					
						
							|  |  |  |     },{ .name = "SEU_VIRTUAL_SYNDRM3",  .addr = A_SEU_VIRTUAL_SYNDRM3, | 
					
						
							|  |  |  |     },{ .name = "SEU_CRC0",  .addr = A_SEU_CRC0, | 
					
						
							|  |  |  |     },{ .name = "SEU_CRC1",  .addr = A_SEU_CRC1, | 
					
						
							|  |  |  |     },{ .name = "SEU_CRC2",  .addr = A_SEU_CRC2, | 
					
						
							|  |  |  |     },{ .name = "SEU_CRC3",  .addr = A_SEU_CRC3, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_BOT0",  .addr = A_CFRAME_FAR_BOT0, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_BOT1",  .addr = A_CFRAME_FAR_BOT1, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_BOT2",  .addr = A_CFRAME_FAR_BOT2, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_BOT3",  .addr = A_CFRAME_FAR_BOT3, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_TOP0",  .addr = A_CFRAME_FAR_TOP0, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_TOP1",  .addr = A_CFRAME_FAR_TOP1, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_TOP2",  .addr = A_CFRAME_FAR_TOP2, | 
					
						
							|  |  |  |     },{ .name = "CFRAME_FAR_TOP3",  .addr = A_CFRAME_FAR_TOP3, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_BOT0",  .addr = A_LAST_FRAME_BOT0, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_bot_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_BOT1",  .addr = A_LAST_FRAME_BOT1, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_bot_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_BOT2",  .addr = A_LAST_FRAME_BOT2, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_bot_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_BOT3",  .addr = A_LAST_FRAME_BOT3, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_bot_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_TOP0",  .addr = A_LAST_FRAME_TOP0, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_top_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_TOP1",  .addr = A_LAST_FRAME_TOP1, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_top_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_TOP2",  .addr = A_LAST_FRAME_TOP2, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_top_post_read, | 
					
						
							|  |  |  |     },{ .name = "LAST_FRAME_TOP3",  .addr = A_LAST_FRAME_TOP3, | 
					
						
							|  |  |  |         .ro = 0xffffffff, | 
					
						
							|  |  |  |         .post_read = cfrm_last_frame_top_post_read, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_reg_cfi_transfer_packet(XlnxCfiIf *cfi_if, | 
					
						
							|  |  |  |                                            XlnxCfiPacket *pkt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(cfi_if); | 
					
						
							|  |  |  |     uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!s->row_configured) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (pkt->reg_addr) { | 
					
						
							|  |  |  |     case CFRAME_FAR: | 
					
						
							|  |  |  |         s->regs[R_FAR0] = pkt->data[0]; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case CFRAME_SFR: | 
					
						
							|  |  |  |         s->regs[R_FAR_SFR0] = pkt->data[0]; | 
					
						
							|  |  |  |         register_write(&s->regs_info[R_FAR_SFR3], 0, | 
					
						
							|  |  |  |                        we, object_get_typename(OBJECT(s)), | 
					
						
							|  |  |  |                        XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case CFRAME_FDRI: | 
					
						
							|  |  |  |         s->regs[R_FDRI0] = pkt->data[0]; | 
					
						
							|  |  |  |         s->regs[R_FDRI1] = pkt->data[1]; | 
					
						
							|  |  |  |         s->regs[R_FDRI2] = pkt->data[2]; | 
					
						
							|  |  |  |         register_write(&s->regs_info[R_FDRI3], pkt->data[3], | 
					
						
							|  |  |  |                        we, object_get_typename(OBJECT(s)), | 
					
						
							|  |  |  |                        XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case CFRAME_CMD: | 
					
						
							|  |  |  |         ARRAY_FIELD_DP32(s->regs, CMD0, CMD, pkt->data[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         register_write(&s->regs_info[R_CMD3], 0, | 
					
						
							|  |  |  |                        we, object_get_typename(OBJECT(s)), | 
					
						
							|  |  |  |                        XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cframe_reg_fdri_read(void *opaque, hwaddr addr, unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" | 
					
						
							|  |  |  |                   HWADDR_PRIx "\n", __func__, addr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_reg_fdri_write(void *opaque, hwaddr addr, uint64_t value, | 
					
						
							|  |  |  |                       unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(opaque); | 
					
						
							|  |  |  |     uint32_t wfifo[WFIFO_SZ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (update_wfifo(addr, value, s->wfifo, wfifo)) { | 
					
						
							|  |  |  |         uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         s->regs[R_FDRI0] = wfifo[0]; | 
					
						
							|  |  |  |         s->regs[R_FDRI1] = wfifo[1]; | 
					
						
							|  |  |  |         s->regs[R_FDRI2] = wfifo[2]; | 
					
						
							|  |  |  |         register_write(&s->regs_info[R_FDRI3], wfifo[3], | 
					
						
							|  |  |  |                        we, object_get_typename(OBJECT(s)), | 
					
						
							|  |  |  |                        XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_reg_reset_enter(Object *obj, ResetType type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); | 
					
						
							|  |  |  |     unsigned int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { | 
					
						
							|  |  |  |         register_reset(&s->regs_info[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); | 
					
						
							|  |  |  |     fifo32_reset(&s->new_f_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (g_tree_nnodes(s->cframes)) { | 
					
						
							|  |  |  |         /*
 | 
					
						
							|  |  |  |          * Take a reference so when g_tree_destroy() unrefs it we keep the | 
					
						
							|  |  |  |          * GTree and only destroy its contents. NB: when our minimum | 
					
						
							|  |  |  |          * glib version is at least 2.70 we could use g_tree_remove_all(). | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         g_tree_ref(s->cframes); | 
					
						
							|  |  |  |         g_tree_destroy(s->cframes); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-12 17:08:07 +01:00
										 |  |  | static void cframe_reg_reset_hold(Object *obj, ResetType type) | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cfrm_imr_update_irq(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps cframe_reg_ops = { | 
					
						
							|  |  |  |     .read = register_read_memory, | 
					
						
							|  |  |  |     .write = register_write_memory, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps cframe_reg_fdri_ops = { | 
					
						
							|  |  |  |     .read = cframe_reg_fdri_read, | 
					
						
							|  |  |  |     .write = cframe_reg_fdri_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  | static uint64_t cframes_bcast_reg_read(void *opaque, hwaddr addr, unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" | 
					
						
							|  |  |  |                   HWADDR_PRIx "\n", __func__, addr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframes_bcast_write(XlnxVersalCFrameBcastReg *s, uint8_t reg_addr, | 
					
						
							|  |  |  |                                 uint32_t *wfifo) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxCfiPacket pkt = { | 
					
						
							|  |  |  |         .reg_addr = reg_addr, | 
					
						
							|  |  |  |         .data[0] = wfifo[0], | 
					
						
							|  |  |  |         .data[1] = wfifo[1], | 
					
						
							|  |  |  |         .data[2] = wfifo[2], | 
					
						
							|  |  |  |         .data[3] = wfifo[3] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < ARRAY_SIZE(s->cfg.cframe); i++) { | 
					
						
							|  |  |  |         if (s->cfg.cframe[i]) { | 
					
						
							|  |  |  |             xlnx_cfi_transfer_packet(s->cfg.cframe[i], &pkt); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframes_bcast_reg_write(void *opaque, hwaddr addr, uint64_t value, | 
					
						
							|  |  |  |                       unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(opaque); | 
					
						
							|  |  |  |     uint32_t wfifo[WFIFO_SZ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (update_wfifo(addr, value, s->wfifo, wfifo)) { | 
					
						
							|  |  |  |         uint8_t reg_addr = extract32(addr, 4, 6); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cframes_bcast_write(s, reg_addr, wfifo); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint64_t cframes_bcast_fdri_read(void *opaque, hwaddr addr, | 
					
						
							|  |  |  |                                         unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" | 
					
						
							|  |  |  |                   HWADDR_PRIx "\n", __func__, addr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframes_bcast_fdri_write(void *opaque, hwaddr addr, uint64_t value, | 
					
						
							|  |  |  |                       unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(opaque); | 
					
						
							|  |  |  |     uint32_t wfifo[WFIFO_SZ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (update_wfifo(addr, value, s->wfifo, wfifo)) { | 
					
						
							|  |  |  |         cframes_bcast_write(s, CFRAME_FDRI, wfifo); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps cframes_bcast_reg_reg_ops = { | 
					
						
							|  |  |  |     .read = cframes_bcast_reg_read, | 
					
						
							|  |  |  |     .write = cframes_bcast_reg_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps cframes_bcast_reg_fdri_ops = { | 
					
						
							|  |  |  |     .read = cframes_bcast_fdri_read, | 
					
						
							|  |  |  |     .write = cframes_bcast_fdri_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | static void cframe_reg_realize(DeviceState *dev, Error **errp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < ARRAY_SIZE(s->cfg.blktype_num_frames); i++) { | 
					
						
							|  |  |  |         if (s->cfg.blktype_num_frames[i] > MAX_BLOCKTYPE_FRAMES) { | 
					
						
							|  |  |  |             error_setg(errp, | 
					
						
							|  |  |  |                        "blktype-frames%d > 0xFFFFF (max frame per block)", | 
					
						
							|  |  |  |                        i); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (s->cfg.blktype_num_frames[i]) { | 
					
						
							|  |  |  |             s->row_configured = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_reg_init(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); | 
					
						
							|  |  |  |     SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | 
					
						
							|  |  |  |     RegisterInfoArray *reg_array; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memory_region_init(&s->iomem, obj, TYPE_XLNX_VERSAL_CFRAME_REG, | 
					
						
							|  |  |  |                        CFRAME_REG_R_MAX * 4); | 
					
						
							|  |  |  |     reg_array = | 
					
						
							|  |  |  |         register_init_block32(DEVICE(obj), cframe_reg_regs_info, | 
					
						
							|  |  |  |                               ARRAY_SIZE(cframe_reg_regs_info), | 
					
						
							|  |  |  |                               s->regs_info, s->regs, | 
					
						
							|  |  |  |                               &cframe_reg_ops, | 
					
						
							|  |  |  |                               XLNX_VERSAL_CFRAME_REG_ERR_DEBUG, | 
					
						
							|  |  |  |                               CFRAME_REG_R_MAX * 4); | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->iomem, | 
					
						
							|  |  |  |                                 0x0, | 
					
						
							|  |  |  |                                 ®_array->mem); | 
					
						
							|  |  |  |     sysbus_init_mmio(sbd, &s->iomem); | 
					
						
							|  |  |  |     memory_region_init_io(&s->iomem_fdri, obj, &cframe_reg_fdri_ops, s, | 
					
						
							|  |  |  |                           TYPE_XLNX_VERSAL_CFRAME_REG "-fdri", | 
					
						
							|  |  |  |                           KEYHOLE_STREAM_4K); | 
					
						
							|  |  |  |     sysbus_init_mmio(sbd, &s->iomem_fdri); | 
					
						
							|  |  |  |     sysbus_init_irq(sbd, &s->irq_cfrm_imr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->cframes = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, | 
					
						
							|  |  |  |                                   NULL, (GDestroyNotify)g_free); | 
					
						
							|  |  |  |     fifo32_create(&s->new_f_data, FRAME_NUM_WORDS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const VMStateDescription vmstate_cframe = { | 
					
						
							|  |  |  |     .name = "cframe", | 
					
						
							|  |  |  |     .version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							| 
									
										
										
										
											2023-12-21 14:16:21 +11:00
										 |  |  |     .fields = (const VMStateField[]) { | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  |         VMSTATE_UINT32_ARRAY(data, XlnxCFrame, FRAME_NUM_WORDS), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const VMStateDescription vmstate_cframe_reg = { | 
					
						
							|  |  |  |     .name = TYPE_XLNX_VERSAL_CFRAME_REG, | 
					
						
							|  |  |  |     .version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							| 
									
										
										
										
											2023-12-21 14:16:21 +11:00
										 |  |  |     .fields = (const VMStateField[]) { | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  |         VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameReg, 4), | 
					
						
							|  |  |  |         VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFrameReg, CFRAME_REG_R_MAX), | 
					
						
							|  |  |  |         VMSTATE_BOOL(rowon, XlnxVersalCFrameReg), | 
					
						
							|  |  |  |         VMSTATE_BOOL(wcfg, XlnxVersalCFrameReg), | 
					
						
							|  |  |  |         VMSTATE_BOOL(rcfg, XlnxVersalCFrameReg), | 
					
						
							|  |  |  |         VMSTATE_GTREE_DIRECT_KEY_V(cframes, XlnxVersalCFrameReg, 1, | 
					
						
							|  |  |  |                                    &vmstate_cframe, XlnxCFrame), | 
					
						
							|  |  |  |         VMSTATE_FIFO32(new_f_data, XlnxVersalCFrameReg), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST(), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Property cframe_regs_props[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cfu-fdro", XlnxVersalCFrameReg, cfg.cfu_fdro, | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype0-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[0], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype1-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[1], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype2-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[2], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype3-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[3], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype4-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[4], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype5-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[5], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("blktype6-frames", XlnxVersalCFrameReg, | 
					
						
							|  |  |  |                        cfg.blktype_num_frames[6], 0), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  | static void cframe_bcast_reg_init(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(obj); | 
					
						
							|  |  |  |     SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memory_region_init_io(&s->iomem_reg, obj, &cframes_bcast_reg_reg_ops, s, | 
					
						
							|  |  |  |                           TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, KEYHOLE_STREAM_4K); | 
					
						
							|  |  |  |     memory_region_init_io(&s->iomem_fdri, obj, &cframes_bcast_reg_fdri_ops, s, | 
					
						
							|  |  |  |                           TYPE_XLNX_VERSAL_CFRAME_BCAST_REG "-fdri", | 
					
						
							|  |  |  |                           KEYHOLE_STREAM_4K); | 
					
						
							|  |  |  |     sysbus_init_mmio(sbd, &s->iomem_reg); | 
					
						
							|  |  |  |     sysbus_init_mmio(sbd, &s->iomem_fdri); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cframe_bcast_reg_reset_enter(Object *obj, ResetType type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(obj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const VMStateDescription vmstate_cframe_bcast_reg = { | 
					
						
							|  |  |  |     .name = TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, | 
					
						
							|  |  |  |     .version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							| 
									
										
										
										
											2023-12-21 14:16:21 +11:00
										 |  |  |     .fields = (const VMStateField[]) { | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  |         VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameBcastReg, 4), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST(), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Property cframe_bcast_regs_props[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe0", XlnxVersalCFrameBcastReg, cfg.cframe[0], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe1", XlnxVersalCFrameBcastReg, cfg.cframe[1], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe2", XlnxVersalCFrameBcastReg, cfg.cframe[2], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe3", XlnxVersalCFrameBcastReg, cfg.cframe[3], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe4", XlnxVersalCFrameBcastReg, cfg.cframe[4], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe5", XlnxVersalCFrameBcastReg, cfg.cframe[5], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe6", XlnxVersalCFrameBcastReg, cfg.cframe[6], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe7", XlnxVersalCFrameBcastReg, cfg.cframe[7], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe8", XlnxVersalCFrameBcastReg, cfg.cframe[8], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe9", XlnxVersalCFrameBcastReg, cfg.cframe[9], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe10", XlnxVersalCFrameBcastReg, cfg.cframe[10], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe11", XlnxVersalCFrameBcastReg, cfg.cframe[11], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe12", XlnxVersalCFrameBcastReg, cfg.cframe[12], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe13", XlnxVersalCFrameBcastReg, cfg.cframe[13], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("cframe14", XlnxVersalCFrameBcastReg, cfg.cframe[14], | 
					
						
							|  |  |  |                      TYPE_XLNX_CFI_IF, XlnxCfiIf *), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | static void cframe_reg_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ResettableClass *rc = RESETTABLE_CLASS(klass); | 
					
						
							|  |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							|  |  |  |     XlnxCfiIfClass *xcic = XLNX_CFI_IF_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dc->vmsd = &vmstate_cframe_reg; | 
					
						
							|  |  |  |     dc->realize = cframe_reg_realize; | 
					
						
							|  |  |  |     rc->phases.enter = cframe_reg_reset_enter; | 
					
						
							|  |  |  |     rc->phases.hold = cframe_reg_reset_hold; | 
					
						
							|  |  |  |     device_class_set_props(dc, cframe_regs_props); | 
					
						
							|  |  |  |     xcic->cfi_transfer_packet = cframe_reg_cfi_transfer_packet; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  | static void cframe_bcast_reg_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							|  |  |  |     ResettableClass *rc = RESETTABLE_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dc->vmsd = &vmstate_cframe_bcast_reg; | 
					
						
							|  |  |  |     device_class_set_props(dc, cframe_bcast_regs_props); | 
					
						
							|  |  |  |     rc->phases.enter = cframe_bcast_reg_reset_enter; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | static const TypeInfo cframe_reg_info = { | 
					
						
							|  |  |  |     .name          = TYPE_XLNX_VERSAL_CFRAME_REG, | 
					
						
							|  |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(XlnxVersalCFrameReg), | 
					
						
							|  |  |  |     .class_init    = cframe_reg_class_init, | 
					
						
							|  |  |  |     .instance_init = cframe_reg_init, | 
					
						
							|  |  |  |     .interfaces = (InterfaceInfo[]) { | 
					
						
							|  |  |  |         { TYPE_XLNX_CFI_IF }, | 
					
						
							|  |  |  |         { } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  | static const TypeInfo cframe_bcast_reg_info = { | 
					
						
							|  |  |  |     .name          = TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, | 
					
						
							|  |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(XlnxVersalCFrameBcastReg), | 
					
						
							|  |  |  |     .class_init    = cframe_bcast_reg_class_init, | 
					
						
							|  |  |  |     .instance_init = cframe_bcast_reg_init, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | static void cframe_reg_register_types(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     type_register_static(&cframe_reg_info); | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:59 +01:00
										 |  |  |     type_register_static(&cframe_bcast_reg_info); | 
					
						
							| 
									
										
										
										
											2023-08-31 17:56:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(cframe_reg_register_types) |