Files
Yaya48 cf256aa081 Import QEMU upstream snapshot d2e570c
Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
2026-08-31 02:15:30 +02:00

557 lines
16 KiB
C

/*
* Kendryte K230 DDR controller and PHY models
*
* Models the K230 DDRC CFG registers and K230 DDR PHY registers exercised
* by the K230 SDK U-Boot SPL.
*
* Copyright (c) 2026 Junze Cao <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "migration/vmstate.h"
#include "hw/core/registerfields.h"
#include "hw/core/resettable.h"
#include "hw/misc/k230_ddr.h"
/* DDRC CFG registers */
enum K230DDRCOperatingMode {
K230_DDRC_MODE_INIT = 0,
K230_DDRC_MODE_NORMAL = 1,
};
REG32(K230_DDRC_MSTR, 0x000)
REG32(K230_DDRC_STAT, 0x004)
FIELD(K230_DDRC_STAT, OPERATING_MODE, 0, 3)
REG32(K230_DDRC_PWRCTL, 0x030)
REG32(K230_DDRC_RFSHCTL0, 0x050)
REG32(K230_DDRC_RFSHTMG, 0x064)
REG32(K230_DDRC_RFSHTMG1, 0x068)
REG32(K230_DDRC_INIT0, 0x0d0)
REG32(K230_DDRC_INIT2, 0x0d8)
REG32(K230_DDRC_INIT3, 0x0dc)
REG32(K230_DDRC_INIT5, 0x0e4)
REG32(K230_DDRC_DRAMTMG0, 0x100)
#define R_K230_DDRC_DRAMTMG(index) \
(R_K230_DDRC_DRAMTMG0 + (index))
REG32(K230_DDRC_ZQCTL0, 0x180)
REG32(K230_DDRC_ZQCTL1, 0x184)
REG32(K230_DDRC_ZQCTL2, 0x188)
REG32(K230_DDRC_ZQSTAT, 0x18c)
REG32(K230_DDRC_DFITMG0, 0x190)
REG32(K230_DDRC_DFITMG1, 0x194)
REG32(K230_DDRC_DFIUPD0, 0x1a0)
REG32(K230_DDRC_DFIUPD1, 0x1a4)
REG32(K230_DDRC_DFIUPD2, 0x1a8)
REG32(K230_DDRC_DFIMISC, 0x1b0)
FIELD(K230_DDRC_DFIMISC, DFI_INIT_COMPLETE_EN, 0, 1)
FIELD(K230_DDRC_DFIMISC, DFI_INIT_START, 5, 1)
REG32(K230_DDRC_DFITMG2, 0x1b4)
REG32(K230_DDRC_DFISTAT, 0x1bc)
FIELD(K230_DDRC_DFISTAT, DFI_INIT_COMPLETE, 0, 1)
REG32(K230_DDRC_ODTCFG, 0x240)
REG32(K230_DDRC_SWCTL, 0x320)
FIELD(K230_DDRC_SWCTL, SW_DONE, 0, 1)
REG32(K230_DDRC_SWSTAT, 0x324)
FIELD(K230_DDRC_SWSTAT, SW_DONE_ACK, 0, 1)
static const uint32_t k230_ddrc_reset_values[K230_DDRC_REG_COUNT] = {
[R_K230_DDRC_MSTR] = 0x01040000,
[R_K230_DDRC_RFSHCTL0] = 0x00210000,
[R_K230_DDRC_RFSHTMG] = 0x0062008c,
[R_K230_DDRC_RFSHTMG1] = 0x0000008c,
[R_K230_DDRC_INIT0] = 0x0002004e,
[R_K230_DDRC_INIT3] = 0x00000510,
[R_K230_DDRC_INIT5] = 0x00100000,
[R_K230_DDRC_DRAMTMG(0)] = 0x0f101b0f,
[R_K230_DDRC_DRAMTMG(1)] = 0x00080414,
[R_K230_DDRC_DRAMTMG(2)] = 0x0305060d,
[R_K230_DDRC_DRAMTMG(3)] = 0x00004000,
[R_K230_DDRC_DRAMTMG(4)] = 0x05040405,
[R_K230_DDRC_DRAMTMG(5)] = 0x05050403,
[R_K230_DDRC_DRAMTMG(6)] = 0x02020005,
[R_K230_DDRC_DRAMTMG(7)] = 0x00000202,
[R_K230_DDRC_DRAMTMG(8)] = 0x03034405,
[R_K230_DDRC_DRAMTMG(9)] = 0x0004040d,
[R_K230_DDRC_DRAMTMG(10)] = 0x001c180a,
[R_K230_DDRC_DRAMTMG(11)] = 0x440c021c,
[R_K230_DDRC_DRAMTMG(12)] = 0x00020610,
[R_K230_DDRC_DRAMTMG(13)] = 0x1c200004,
[R_K230_DDRC_DRAMTMG(14)] = 0x000000a0,
[R_K230_DDRC_DRAMTMG(16)] = 0x05100404,
[R_K230_DDRC_ZQCTL0] = 0x02000040,
[R_K230_DDRC_ZQCTL1] = 0x02000100,
[R_K230_DDRC_DFITMG0] = 0x07020002,
[R_K230_DDRC_DFITMG1] = 0x00000404,
[R_K230_DDRC_DFIUPD0] = 0x00400003,
[R_K230_DDRC_DFIUPD1] = 0x00010001,
[R_K230_DDRC_DFIUPD2] = 0x80000000,
[R_K230_DDRC_DFIMISC] = 0x00000001,
[R_K230_DDRC_DFITMG2] = 0x00000202,
[R_K230_DDRC_ODTCFG] = 0x04000400,
[R_K230_DDRC_SWCTL] = 0x00000001,
[R_K230_DDRC_SWSTAT] = 0x00000001,
};
/* K230 DDR PHY CSRs use byte offset = CSR index * 4. */
#define K230_DDR_PHY_CSR_OFFSET(index) ((index) * 4)
#define K230_DDR_PHY_MASTER_CSR(reg) \
K230_DDR_PHY_CSR_OFFSET(0x20000 + (reg))
#define K230_DDR_PHY_APBONLY_CSR(reg) \
K230_DDR_PHY_CSR_OFFSET(0xd0000 + (reg))
#define K230_DDR_PHY_ATX_IMPEDANCE_RESET UINT32_C(0x03ff)
REG32(K230_DDR_PHY_MICRO_CONT_MUX_SEL,
K230_DDR_PHY_APBONLY_CSR(0x000))
FIELD(K230_DDR_PHY_MICRO_CONT_MUX_SEL, MICRO_CONT_MUX_SEL, 0, 1)
REG32(K230_DDR_PHY_TRAINING_STATUS,
K230_DDR_PHY_APBONLY_CSR(0x004))
FIELD(K230_DDR_PHY_TRAINING_STATUS, MAILBOX_EMPTY, 0, 1)
REG32(K230_DDR_PHY_TRAINING_ACK,
K230_DDR_PHY_APBONLY_CSR(0x031))
REG32(K230_DDR_PHY_TRAINING_MESSAGE,
K230_DDR_PHY_APBONLY_CSR(0x032))
REG32(K230_DDR_PHY_TRAINING_TRIGGER,
K230_DDR_PHY_APBONLY_CSR(0x099))
#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET UINT32_C(0x0fff)
REG32(K230_DDR_PHY_DFI_INIT_COMPLETE,
K230_DDR_PHY_MASTER_CSR(0x0f9))
FIELD(K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 0, 1)
REG32(K230_DDR_PHY_VREF_IN_GLOBAL,
K230_DDR_PHY_MASTER_CSR(0x0b2))
#define K230_DDR_PHY_VREF_IN_GLOBAL_RESET UINT32_C(0x0200)
static void k230_ddr_cfg_update_state(K230DDRCfgState *s)
{
bool dfi_complete = false;
unsigned int operating_mode = K230_DDRC_MODE_INIT;
if (s->phy) {
dfi_complete = FIELD_EX32(s->phy->dfi_init_complete,
K230_DDR_PHY_DFI_INIT_COMPLETE,
DFI_INIT_COMPLETE);
}
ARRAY_FIELD_DP32(s->regs, K230_DDRC_DFISTAT,
DFI_INIT_COMPLETE, dfi_complete);
ARRAY_FIELD_DP32(s->regs, K230_DDRC_SWSTAT, SW_DONE_ACK,
ARRAY_FIELD_EX32(s->regs, K230_DDRC_SWCTL, SW_DONE));
if (dfi_complete &&
ARRAY_FIELD_EX32(s->regs, K230_DDRC_DFIMISC,
DFI_INIT_COMPLETE_EN)) {
s->initialized = true;
}
if (s->initialized) {
operating_mode = K230_DDRC_MODE_NORMAL;
}
ARRAY_FIELD_DP32(s->regs, K230_DDRC_STAT,
OPERATING_MODE, operating_mode);
}
static uint64_t k230_ddr_cfg_read(void *opaque, hwaddr addr, unsigned int size)
{
K230DDRCfgState *s = opaque;
uint64_t value = 0;
if (addr < K230_DDRC_REG_SIZE) {
k230_ddr_cfg_update_state(s);
value = s->regs[addr / sizeof(uint32_t)];
}
return value;
}
static void k230_ddr_cfg_write(void *opaque, hwaddr addr, uint64_t value,
unsigned int size)
{
K230DDRCfgState *s = opaque;
uint32_t val = value;
if (addr >= K230_DDRC_REG_SIZE) {
return;
}
switch (addr) {
case A_K230_DDRC_STAT:
case A_K230_DDRC_DFISTAT:
case A_K230_DDRC_ZQSTAT:
case A_K230_DDRC_SWSTAT:
return;
case A_K230_DDRC_PWRCTL:
s->regs[R_K230_DDRC_PWRCTL] = val & MAKE_64BIT_MASK(0, 9);
break;
case A_K230_DDRC_ZQCTL2:
/* ZQ reset completes immediately and the W1S bit self-clears. */
s->regs[R_K230_DDRC_ZQCTL2] = 0;
s->regs[R_K230_DDRC_ZQSTAT] = 0;
break;
case A_K230_DDRC_DFIMISC:
s->regs[R_K230_DDRC_DFIMISC] =
val & (MAKE_64BIT_MASK(0, 6) | MAKE_64BIT_MASK(8, 5));
if (s->phy && s->phy->training_complete &&
FIELD_EX32(val, K230_DDRC_DFIMISC, DFI_INIT_START)) {
s->phy->dfi_init_complete = FIELD_DP32(
s->phy->dfi_init_complete,
K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 1);
}
break;
case A_K230_DDRC_SWCTL:
s->regs[R_K230_DDRC_SWCTL] =
val & R_K230_DDRC_SWCTL_SW_DONE_MASK;
break;
default:
s->regs[addr / sizeof(uint32_t)] = val;
break;
}
k230_ddr_cfg_update_state(s);
}
static bool k230_ddr_phy_decode_anib(hwaddr addr, unsigned int *anib)
{
uint32_t csr = addr / sizeof(uint32_t);
if ((csr & 0xfff) != 0x043) {
return false;
}
*anib = csr >> 12;
return *anib < K230_DDR_PHY_ANIB_COUNT;
}
static bool k230_ddr_phy_decode_dbyte(hwaddr addr, uint32_t reg,
unsigned int *dbyte,
unsigned int *nibble)
{
uint32_t csr = addr / sizeof(uint32_t);
uint32_t block_offset;
if (csr < 0x10000) {
return false;
}
csr -= 0x10000;
*dbyte = csr >> 12;
if (*dbyte >= K230_DDR_PHY_DBYTE_COUNT) {
return false;
}
block_offset = csr & 0xfff;
*nibble = block_offset >> 8;
return *nibble < K230_DDR_PHY_NIBBLES_PER_DBYTE &&
(block_offset & 0xff) == reg;
}
static uint64_t k230_ddr_phy_read(void *opaque, hwaddr addr,
unsigned int size)
{
K230DDRPhyState *s = opaque;
unsigned int dbyte;
unsigned int nibble;
unsigned int anib;
uint64_t value = 0;
switch (addr) {
case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL:
value = s->micro_cont_mux_sel;
break;
case A_K230_DDR_PHY_TRAINING_STATUS:
value = s->mailbox_message_pending ? 0 :
R_K230_DDR_PHY_TRAINING_STATUS_MAILBOX_EMPTY_MASK;
break;
case A_K230_DDR_PHY_TRAINING_MESSAGE:
value = s->training_complete ? 0x07 : 0;
break;
case A_K230_DDR_PHY_TRAINING_ACK:
case A_K230_DDR_PHY_TRAINING_TRIGGER:
break;
case A_K230_DDR_PHY_DFI_INIT_COMPLETE:
value = s->dfi_init_complete;
break;
case A_K230_DDR_PHY_VREF_IN_GLOBAL:
value = s->vref_in_global;
break;
default:
if (k230_ddr_phy_decode_anib(addr, &anib)) {
value = s->atx_impedance[anib];
} else if (k230_ddr_phy_decode_dbyte(addr, 0x049,
&dbyte, &nibble)) {
value = s->tx_impedance_ctrl1[dbyte][nibble];
} else if (k230_ddr_phy_decode_dbyte(addr, 0x04d,
&dbyte, &nibble)) {
value = s->tx_odt_drv_stren[dbyte][nibble];
} else {
qemu_log_mask(LOG_UNIMP,
"%s: unimplemented DDR PHY read at offset 0x%"
HWADDR_PRIx ", returning 0\n",
__func__, addr);
}
break;
}
return value;
}
static void k230_ddr_phy_write(void *opaque, hwaddr addr, uint64_t value,
unsigned int size)
{
K230DDRPhyState *s = opaque;
uint16_t val = value;
unsigned int dbyte;
unsigned int nibble;
unsigned int anib;
switch (addr) {
case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL:
s->micro_cont_mux_sel =
val & R_K230_DDR_PHY_MICRO_CONT_MUX_SEL_MICRO_CONT_MUX_SEL_MASK;
return;
case A_K230_DDR_PHY_TRAINING_STATUS:
case A_K230_DDR_PHY_TRAINING_MESSAGE:
return;
case A_K230_DDR_PHY_TRAINING_ACK:
if (val == 0) {
s->mailbox_message_pending = false;
}
return;
case A_K230_DDR_PHY_TRAINING_TRIGGER:
if (val != 0) {
s->training_trigger_seen = true;
} else if (s->training_trigger_seen) {
/* Abstract the loaded firmware run to its final mailbox message. */
s->training_trigger_seen = false;
s->training_complete = true;
s->mailbox_message_pending = true;
}
return;
default:
break;
}
if (FIELD_EX32(s->micro_cont_mux_sel,
K230_DDR_PHY_MICRO_CONT_MUX_SEL,
MICRO_CONT_MUX_SEL)) {
return;
}
switch (addr) {
case A_K230_DDR_PHY_DFI_INIT_COMPLETE:
s->dfi_init_complete =
val & R_K230_DDR_PHY_DFI_INIT_COMPLETE_DFI_INIT_COMPLETE_MASK;
return;
case A_K230_DDR_PHY_VREF_IN_GLOBAL:
s->vref_in_global = val & UINT16_C(0x7fff);
return;
default:
break;
}
if (k230_ddr_phy_decode_anib(addr, &anib)) {
s->atx_impedance[anib] = val & UINT16_C(0x03ff);
return;
}
if (k230_ddr_phy_decode_dbyte(addr, 0x049, &dbyte, &nibble)) {
s->tx_impedance_ctrl1[dbyte][nibble] = val & UINT16_C(0x0fff);
return;
}
if (k230_ddr_phy_decode_dbyte(addr, 0x04d, &dbyte, &nibble)) {
s->tx_odt_drv_stren[dbyte][nibble] = val & UINT16_C(0x0fff);
return;
}
qemu_log_mask(LOG_UNIMP,
"%s: unimplemented DDR PHY write at offset 0x%"
HWADDR_PRIx ", value 0x%" PRIx64 "\n",
__func__, addr, value);
}
static const MemoryRegionOps k230_ddr_cfg_ops = {
.read = k230_ddr_cfg_read,
.write = k230_ddr_cfg_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static const MemoryRegionOps k230_ddr_phy_ops = {
.read = k230_ddr_phy_read,
.write = k230_ddr_phy_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void k230_ddr_cfg_init(Object *obj)
{
K230DDRCfgState *s = K230_DDR_CFG(obj);
memory_region_init_io(&s->mmio, obj, &k230_ddr_cfg_ops, s,
"k230-ddr-cfg", K230_DDRC_MMIO_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
static void k230_ddr_phy_init(Object *obj)
{
K230DDRPhyState *s = K230_DDR_PHY(obj);
memory_region_init_io(&s->mmio, obj, &k230_ddr_phy_ops, s,
"k230-ddr-phy", K230_DDR_PHY_MMIO_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
static void k230_ddr_cfg_reset_enter(Object *obj, ResetType type)
{
K230DDRCfgState *s = K230_DDR_CFG(obj);
s->initialized = false;
memcpy(s->regs, k230_ddrc_reset_values, sizeof(s->regs));
}
static void k230_ddr_phy_reset_enter(Object *obj, ResetType type)
{
K230DDRPhyState *s = K230_DDR_PHY(obj);
unsigned int dbyte;
unsigned int nibble;
unsigned int anib;
memset(s->tx_odt_drv_stren, 0, sizeof(s->tx_odt_drv_stren));
for (anib = 0; anib < ARRAY_SIZE(s->atx_impedance); anib++) {
s->atx_impedance[anib] = K230_DDR_PHY_ATX_IMPEDANCE_RESET;
}
for (dbyte = 0; dbyte < ARRAY_SIZE(s->tx_impedance_ctrl1); dbyte++) {
for (nibble = 0;
nibble < ARRAY_SIZE(s->tx_impedance_ctrl1[dbyte]); nibble++) {
s->tx_impedance_ctrl1[dbyte][nibble] =
K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET;
}
}
s->dfi_init_complete = 0;
s->vref_in_global = K230_DDR_PHY_VREF_IN_GLOBAL_RESET;
s->micro_cont_mux_sel = 0;
s->training_trigger_seen = false;
s->training_complete = false;
s->mailbox_message_pending = false;
}
static const VMStateDescription vmstate_k230_ddr_cfg = {
.name = "k230-ddr-cfg",
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, K230DDRCfgState, K230_DDRC_REG_COUNT),
VMSTATE_BOOL(initialized, K230DDRCfgState),
VMSTATE_END_OF_LIST()
},
};
static const VMStateDescription vmstate_k230_ddr_phy = {
.name = "k230-ddr-phy",
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT16_ARRAY(atx_impedance, K230DDRPhyState,
K230_DDR_PHY_ANIB_COUNT),
VMSTATE_UINT16_2DARRAY(tx_impedance_ctrl1, K230DDRPhyState,
K230_DDR_PHY_DBYTE_COUNT,
K230_DDR_PHY_NIBBLES_PER_DBYTE),
VMSTATE_UINT16_2DARRAY(tx_odt_drv_stren, K230DDRPhyState,
K230_DDR_PHY_DBYTE_COUNT,
K230_DDR_PHY_NIBBLES_PER_DBYTE),
VMSTATE_UINT16(dfi_init_complete, K230DDRPhyState),
VMSTATE_UINT16(vref_in_global, K230DDRPhyState),
VMSTATE_UINT16(micro_cont_mux_sel, K230DDRPhyState),
VMSTATE_BOOL(training_trigger_seen, K230DDRPhyState),
VMSTATE_BOOL(training_complete, K230DDRPhyState),
VMSTATE_BOOL(mailbox_message_pending, K230DDRPhyState),
VMSTATE_END_OF_LIST()
},
};
static void k230_ddr_cfg_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.enter = k230_ddr_cfg_reset_enter;
dc->vmsd = &vmstate_k230_ddr_cfg;
}
static void k230_ddr_phy_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.enter = k230_ddr_phy_reset_enter;
dc->vmsd = &vmstate_k230_ddr_phy;
}
static const TypeInfo k230_ddr_types[] = {
{
.name = TYPE_K230_DDR_CFG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(K230DDRCfgState),
.instance_init = k230_ddr_cfg_init,
.class_init = k230_ddr_cfg_class_init,
},
{
.name = TYPE_K230_DDR_PHY,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(K230DDRPhyState),
.instance_init = k230_ddr_phy_init,
.class_init = k230_ddr_phy_class_init,
},
};
DEFINE_TYPES(k230_ddr_types)