ad80e36744
We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Luc Michel <luc.michel@amd.com> Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
859 lines
29 KiB
C
859 lines
29 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
static void cframe_reg_reset_hold(Object *obj, ResetType type)
|
|
{
|
|
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,
|
|
},
|
|
};
|
|
|
|
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,
|
|
},
|
|
};
|
|
|
|
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,
|
|
.fields = (const VMStateField[]) {
|
|
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,
|
|
.fields = (const VMStateField[]) {
|
|
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(),
|
|
};
|
|
|
|
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,
|
|
.fields = (const VMStateField[]) {
|
|
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(),
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 },
|
|
{ }
|
|
}
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
static void cframe_reg_register_types(void)
|
|
{
|
|
type_register_static(&cframe_reg_info);
|
|
type_register_static(&cframe_bcast_reg_info);
|
|
}
|
|
|
|
type_init(cframe_reg_register_types)
|