From: Chao Liu <chao.liu@processmission.com>
To: Junze Cao <caojunze424@gmail.com>
Cc: qemu-devel@nongnu.org, qemu-riscv@nongnu.org,
pierrick.bouvier@oss.qualcomm.com, palmer@dabbelt.com,
alistair.francis@wdc.com, liwei1518@gmail.com,
daniel.barboza@oss.qualcomm.com, zhiwei_liu@linux.alibaba.com,
farosas@suse.de, lvivier@redhat.com, pbonzini@redhat.com
Subject: Re: [PATCH 1/3] hw/misc: Add K230 DDR controller and PHY models
Date: Thu, 23 Jul 2026 16:41:25 +0800 [thread overview]
Message-ID: <amHToqJqAYFWtDvL@ChaodeMacBook-Pro.local> (raw)
In-Reply-To: <20260716132423.931427-2-caojunze424@gmail.com>
On Thu, Jul 16, 2026 at 09:24:21PM +0800, Junze Cao wrote:
> The K230 SDK U-Boot SPL programs K230 DDRC CFG and K230 DDR PHY
> registers before DRAM can be used.
>
> Add separate SysBus devices for both register ranges. Model controller
> reset values, DFI and software-update handshakes. For the PHY, model
> register ownership, the training mailbox, and DFI completion.
>
> Include migration state for both devices.
>
> Signed-off-by: Junze Cao <caojunze424@gmail.com>
Suggested-by: Chao Liu <chao.liu@processmission.com>
Thanks,
Chao
> ---
> MAINTAINERS | 2 +
> hw/misc/k230_ddr.c | 544 +++++++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/misc/k230_ddr.h | 59 ++++
> 4 files changed, 606 insertions(+)
> create mode 100644 hw/misc/k230_ddr.c
> create mode 100644 include/hw/misc/k230_ddr.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ecb8cfdc41..1cb3ef6c02 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1826,8 +1826,10 @@ M: Chao Liu <chao.liu@processmission.com>
> L: qemu-riscv@nongnu.org
> S: Maintained
> F: docs/system/riscv/k230.rst
> +F: hw/misc/k230_ddr.c
> F: hw/riscv/k230.c
> F: hw/watchdog/k230_wdt.c
> +F: include/hw/misc/k230_ddr.h
> F: include/hw/riscv/k230.h
> F: include/hw/watchdog/k230_wdt.h
> F: tests/functional/riscv64/test_k230.py
> diff --git a/hw/misc/k230_ddr.c b/hw/misc/k230_ddr.c
> new file mode 100644
> index 0000000000..9ff2714e34
> --- /dev/null
> +++ b/hw/misc/k230_ddr.c
> @@ -0,0 +1,544 @@
> +/*
> + * 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 <caojunze424@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.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];
> + }
> + 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);
> + }
> +}
> +
> +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)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 23265f6035..6d5e2b2647 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -28,6 +28,7 @@ system_ss.add(when: 'CONFIG_IOSB', if_true: files('iosb.c'))
> system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c'))
>
> # RISC-V devices
> +system_ss.add(when: 'CONFIG_K230', if_true: files('k230_ddr.c'))
> system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c'))
> system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c'))
> system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c'))
> diff --git a/include/hw/misc/k230_ddr.h b/include/hw/misc/k230_ddr.h
> new file mode 100644
> index 0000000000..d912e8e1b0
> --- /dev/null
> +++ b/include/hw/misc/k230_ddr.h
> @@ -0,0 +1,59 @@
> +/*
> + * Kendryte K230 DDR controller and PHY models
> + *
> + * Device state and type declarations for the K230 DDRC CFG and K230 DDR PHY
> + * registers.
> + *
> + * Copyright (c) 2026 Junze Cao <caojunze424@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_K230_DDR_H
> +#define HW_MISC_K230_DDR_H
> +
> +#include "hw/core/sysbus.h"
> +
> +#define K230_DDRC_MMIO_SIZE 0x02000000
> +#define K230_DDR_PHY_MMIO_SIZE 0x00400000
> +
> +#define K230_DDRC_REG_SIZE 0x328
> +#define K230_DDRC_REG_COUNT (K230_DDRC_REG_SIZE / sizeof(uint32_t))
> +
> +#define K230_DDR_PHY_ANIB_COUNT 10
> +#define K230_DDR_PHY_DBYTE_COUNT 4
> +#define K230_DDR_PHY_NIBBLES_PER_DBYTE 2
> +
> +#define TYPE_K230_DDR_CFG "riscv.k230.ddr-cfg"
> +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRCfgState, K230_DDR_CFG)
> +
> +#define TYPE_K230_DDR_PHY "riscv.k230.ddr-phy"
> +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRPhyState, K230_DDR_PHY)
> +
> +struct K230DDRCfgState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mmio;
> + K230DDRPhyState *phy;
> + uint32_t regs[K230_DDRC_REG_COUNT];
> + bool initialized;
> +};
> +
> +struct K230DDRPhyState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mmio;
> + uint16_t atx_impedance[K230_DDR_PHY_ANIB_COUNT];
> + uint16_t tx_impedance_ctrl1[K230_DDR_PHY_DBYTE_COUNT]
> + [K230_DDR_PHY_NIBBLES_PER_DBYTE];
> + uint16_t tx_odt_drv_stren[K230_DDR_PHY_DBYTE_COUNT]
> + [K230_DDR_PHY_NIBBLES_PER_DBYTE];
> + uint16_t dfi_init_complete;
> + uint16_t vref_in_global;
> + uint16_t micro_cont_mux_sel;
> + bool training_trigger_seen;
> + bool training_complete;
> + bool mailbox_message_pending;
> +};
> +
> +#endif
> --
> 2.53.0
>
next prev parent reply other threads:[~2026-07-23 8:42 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-16 13:24 [PATCH 0/3] riscv: Add K230 DDR controller and PHY models Junze Cao
2026-07-16 13:24 ` [PATCH 1/3] hw/misc: " Junze Cao
2026-07-22 20:06 ` Daniel Henrique Barboza
2026-07-23 8:41 ` Chao Liu [this message]
2026-07-16 13:24 ` [PATCH 2/3] hw/riscv: Connect " Junze Cao
2026-07-22 20:08 ` Daniel Henrique Barboza
2026-07-23 8:42 ` Chao Liu
2026-07-16 13:24 ` [PATCH 3/3] tests/qtest: Add K230 DDR controller tests Junze Cao
2026-07-22 20:16 ` Daniel Henrique Barboza
2026-07-23 8:42 ` Chao Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=amHToqJqAYFWtDvL@ChaodeMacBook-Pro.local \
--to=chao.liu@processmission.com \
--cc=alistair.francis@wdc.com \
--cc=caojunze424@gmail.com \
--cc=daniel.barboza@oss.qualcomm.com \
--cc=farosas@suse.de \
--cc=liwei1518@gmail.com \
--cc=lvivier@redhat.com \
--cc=palmer@dabbelt.com \
--cc=pbonzini@redhat.com \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox