Re: [PATCH 1/3] hw/misc: Add K230 DDR controller and PHY models
Alistair Francis <[email protected]>
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 2026-08-18 at 11:52 +1000, Alistair Francis wrote: > On Thu, 2026-07-16 at 21:24 +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 <[email protected]> > > This generates a range of failures as part of `make check` > > Summary of Failures: > > 34/557 qemu:func-quick+func-i386 / func-i386-migration > ERROR 0.09s exit status 1 > 36/557 qemu:func-quick+func-i386 / func-i386-empty_cpu_model > ERROR 0.08s exit status 1 > 37/557 qemu:func-quick+func-i386 / func-i386-info_usernet > ERROR 0.09s exit status 1 > 38/557 qemu:func-quick+func-i386 / func-i386-linters > ERROR 0.09s exit status 1 > 39/557 qemu:func-quick+func-i386 / func-i386-monitor_hotplug > ERROR 0.10s exit status 1 > 40/557 qemu:func-quick+func-i386 / func-i386-version > ERROR 0.09s exit status 1 > 41/557 qemu:func-quick+func-i386 / func-i386-vnc > ERROR 0.06s exit status 1 > 42/557 qemu:func-quick+func-x86_64 / func-x86_64-bad_vmstate > ERROR 0.08s exit status 1 > 43/557 qemu:func-quick+func-x86_64 / func-x86_64-cpu_model_versions > ERROR 0.09s exit status 1 > 44/557 qemu:func-quick+func-x86_64 / func-x86_64-cpu_queries > ERROR 0.09s exit status 1 > 45/557 qemu:func-quick+func-x86_64 / func-x86_64-mem_addr_space > ERROR 0.09s exit status 1 > 46/557 qemu:func-quick+func-x86_64 / func-x86_64-migration > ERROR 0.09s exit status 1 > 47/557 qemu:func-quick+func-x86_64 / func-x86_64- > pc_cpu_hotplug_props > ERROR 0.09s exit status 1 > 48/557 qemu:func-quick+func-x86_64 / func-x86_64-virtio_version > ERROR 0.05s exit status 1 > 49/557 qemu:func-quick+func-x86_64 / func-x86_64-vmstate > ERROR 0.09s exit status 1 > 50/557 qemu:func-quick+func-x86_64 / func-x86_64-memlock > ERROR 0.08s exit status 1 > 51/557 qemu:func-quick+func-x86_64 / func-x86_64-empty_cpu_model > ERROR 0.08s exit status 1 > 52/557 qemu:func-quick+func-x86_64 / func-x86_64-info_usernet > ERROR 0.09s exit status 1 > 53/557 qemu:func-quick+func-x86_64 / func-x86_64-linters > ERROR 0.08s exit status 1 > 54/557 qemu:func-quick+func-x86_64 / func-x86_64-monitor_hotplug > ERROR 0.10s exit status 1 > 55/557 qemu:func-quick+func-x86_64 / func-x86_64-version > ERROR 0.09s exit status 1 > 56/557 qemu:func-quick+func-x86_64 / func-x86_64-vnc > ERROR 0.06s exit status 1 > 58/557 qemu:func-quick+func-aarch64 / func-aarch64-migration > ERROR 0.09s exit status 1 > 59/557 qemu:func-quick+func-aarch64 / func-aarch64-vmstate > ERROR 0.09s exit status 1 > 60/557 qemu:func-quick+func-aarch64 / func-aarch64-empty_cpu_model > ERROR 0.09s exit status 1 > 61/557 qemu:func-quick+func-aarch64 / func-aarch64-info_usernet > ERROR 0.09s exit status 1 > 62/557 qemu:func-quick+func-aarch64 / func-aarch64-linters > ERROR 0.09s exit status 1 > 63/557 qemu:func-quick+func-aarch64 / func-aarch64-vnc > ERROR 0.06s exit status 1 > 64/557 qemu:func-quick+func-aarch64 / func-aarch64-monitor_hotplug > ERROR 0.11s exit status 1 > 65/557 qemu:func-quick+func-aarch64 / func-aarch64-version > ERROR 0.10s exit status 1 > 66/557 qemu:func-quick+func-riscv64 / func-riscv64-migration > ERROR 0.09s exit status 1 > 67/557 qemu:func-quick+func-riscv64 / func-riscv64-opensbi > ERROR 0.09s exit status 1 > 68/557 qemu:func-quick+func-riscv64 / func-riscv64-empty_cpu_model > ERROR 0.09s exit status 1 > 69/557 qemu:func-quick+func-riscv64 / func-riscv64-info_usernet > ERROR 0.09s exit status 1 > 70/557 qemu:func-quick+func-riscv64 / func-riscv64-linters > ERROR 0.09s exit status 1 > 71/557 qemu:func-quick+func-riscv64 / func-riscv64-monitor_hotplug > ERROR 0.09s exit status 1 > 72/557 qemu:func-quick+func-riscv64 / func-riscv64-vnc > ERROR 0.05s exit status 1 > 73/557 qemu:func-quick+func-riscv64 / func-riscv64-version > ERROR 0.09s exit status 1 > 74/557 qemu:func-quick+func-riscv32 / func-riscv32-migration > ERROR 0.09s exit status 1 > 75/557 qemu:func-quick+func-riscv32 / func-riscv32-opensbi > ERROR 0.08s exit status 1 > 76/557 qemu:func-quick+func-riscv32 / func-riscv32-empty_cpu_model > ERROR 0.09s exit status 1 > 77/557 qemu:func-quick+func-riscv32 / func-riscv32-info_usernet > ERROR 0.09s exit status 1 > 78/557 qemu:func-quick+func-riscv32 / func-riscv32-linters > ERROR 0.08s exit status 1 > 79/557 qemu:func-quick+func-riscv32 / func-riscv32-monitor_hotplug > ERROR 0.10s exit status 1 > 80/557 qemu:func-quick+func-riscv32 / func-riscv32-version > ERROR 0.08s exit status 1 > 81/557 qemu:func-quick+func-riscv32 / func-riscv32-vnc > ERROR 0.06s exit status 1 > 374/557 qemu:block / io-qcow2-040 > ERROR 0.15s exit status 1 > 377/557 qemu:block / io-qcow2-041 > ERROR 0.15s exit status 1 > 422/557 qemu:block / io-qcow2-141 > ERROR 0.15s exit status 1 > 442/557 qemu:block / io-qcow2-203 > ERROR 0.15s exit status 1 > 452/557 qemu:block / io-qcow2-256 > ERROR 0.15s exit status 1 > 457/557 qemu:block / io-qcow2-283 > ERROR 0.15s exit status 1 > 460/557 qemu:block / io-qcow2-299 > ERROR 0.14s exit status 1 > 465/557 qemu:block / io-qcow2-copy-before-write > ERROR 0.13s exit status 1 > 467/557 qemu:block / io-qcow2-iothreads-commit-active > ERROR 0.17s exit status 1 > 469/557 qemu:block / io-qcow2-iothreads-stream > ERROR 0.17s exit status 1 > 472/557 qemu:block / io-qcow2-nbd-multiconn > ERROR 0.15s exit status 1 > > Ok: 478 > Expected Fail: 0 > Fail: 57 > Unexpected Pass: 0 > Skipped: 22 > Timeout: 0 Nevermind! Seems like that was an issue upstream. Sorry about that Alistair > > > Alistair > > > --- > > 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 <[email protected]> > > L: [email protected] > > 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 <[email protected]> > > + * > > + * 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 <[email protected]> > > + * > > + * 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