[PATCH 04/26] hw/misc: pfsoc: Model L2 cache controller registers

Bin Meng <[email protected]> Thu, 23 Jul 2026 23:18:31 +0800
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Implement the Config, WayEnable, and per-master WayMask registers
described by the PolarFire SoC MSS reference manual. Make WayEnable
monotonic and report the remaining register space as unimplemented.

Signed-off-by: Bin Meng <[email protected]>
---

 MAINTAINERS                       |   2 +
 include/hw/misc/mchp_pfsoc_l2cc.h |  30 +++++
 hw/misc/mchp_pfsoc_l2cc.c         | 180 ++++++++++++++++++++++++++++++
 hw/misc/Kconfig                   |   3 +
 hw/misc/meson.build               |   1 +
 5 files changed, 216 insertions(+)
 create mode 100644 include/hw/misc/mchp_pfsoc_l2cc.h
 create mode 100644 hw/misc/mchp_pfsoc_l2cc.c

diff --git a/MAINTAINERS b/MAINTAINERS
index a28935c898..c0bb18c47a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1770,11 +1770,13 @@ F: hw/riscv/microchip_pfsoc.c
 F: hw/char/mchp_pfsoc_mmuart.c
 F: hw/misc/mchp_pfsoc_dmc.c
 F: hw/misc/mchp_pfsoc_ioscb.c
+F: hw/misc/mchp_pfsoc_l2cc.c
 F: hw/misc/mchp_pfsoc_sysreg.c
 F: include/hw/riscv/microchip_pfsoc.h
 F: include/hw/char/mchp_pfsoc_mmuart.h
 F: include/hw/misc/mchp_pfsoc_dmc.h
 F: include/hw/misc/mchp_pfsoc_ioscb.h
+F: include/hw/misc/mchp_pfsoc_l2cc.h
 F: include/hw/misc/mchp_pfsoc_sysreg.h
 
 Shakti C class SoC
diff --git a/include/hw/misc/mchp_pfsoc_l2cc.h b/include/hw/misc/mchp_pfsoc_l2cc.h
new file mode 100644
index 0000000000..1b5b3888d5
--- /dev/null
+++ b/include/hw/misc/mchp_pfsoc_l2cc.h
@@ -0,0 +1,30 @@
+/*
+ * Microchip PolarFire SoC L2 cache controller
+ *
+ * Copyright (c) 2026 Process Mission
+ *
+ * Author:
+ *   Bin Meng <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef MCHP_PFSOC_L2CC_H
+#define MCHP_PFSOC_L2CC_H
+
+#include "hw/core/register.h"
+#include "hw/core/sysbus.h"
+
+#define MCHP_PFSOC_L2CC_REG_SIZE 0x1000
+#define MCHP_PFSOC_L2CC_REG_NUM  (MCHP_PFSOC_L2CC_REG_SIZE / 8)
+
+typedef struct MchpPfSoCL2ccState {
+    SysBusDevice parent;
+    uint64_t regs[MCHP_PFSOC_L2CC_REG_NUM];
+    RegisterInfo regs_info[MCHP_PFSOC_L2CC_REG_NUM];
+} MchpPfSoCL2ccState;
+
+#define TYPE_MCHP_PFSOC_L2CC "mchp.pfsoc.l2cc"
+OBJECT_DECLARE_SIMPLE_TYPE(MchpPfSoCL2ccState, MCHP_PFSOC_L2CC)
+
+#endif
diff --git a/hw/misc/mchp_pfsoc_l2cc.c b/hw/misc/mchp_pfsoc_l2cc.c
new file mode 100644
index 0000000000..b320356572
--- /dev/null
+++ b/hw/misc/mchp_pfsoc_l2cc.c
@@ -0,0 +1,180 @@
+/*
+ * Microchip PolarFire SoC L2 cache controller
+ *
+ * Copyright (c) 2026 Process Mission
+ *
+ * Author:
+ *   Bin Meng <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/core/register.h"
+#include "hw/core/registerfields.h"
+#include "hw/misc/mchp_pfsoc_l2cc.h"
+
+REG64(L2_CONFIG, 0x000)
+    FIELD(L2_CONFIG, BANKS, 0, 8)
+    FIELD(L2_CONFIG, WAYS, 8, 8)
+    FIELD(L2_CONFIG, SETS, 16, 8)
+    FIELD(L2_CONFIG, BYTES, 24, 8)
+REG64(L2_WAY_ENABLE, 0x008)
+    FIELD(L2_WAY_ENABLE, VALUE, 0, 8)
+REG64(L2_WAY_MASK_DMA, 0x800)
+REG64(L2_WAY_MASK_AXI4_PORT_0, 0x808)
+REG64(L2_WAY_MASK_AXI4_PORT_1, 0x810)
+REG64(L2_WAY_MASK_AXI4_PORT_2, 0x818)
+REG64(L2_WAY_MASK_AXI4_PORT_3, 0x820)
+REG64(L2_WAY_MASK_HART0_DCACHE, 0x828)
+REG64(L2_WAY_MASK_HART0_ICACHE, 0x830)
+REG64(L2_WAY_MASK_HART1_DCACHE, 0x838)
+REG64(L2_WAY_MASK_HART1_ICACHE, 0x840)
+REG64(L2_WAY_MASK_HART2_DCACHE, 0x848)
+REG64(L2_WAY_MASK_HART2_ICACHE, 0x850)
+REG64(L2_WAY_MASK_HART3_DCACHE, 0x858)
+REG64(L2_WAY_MASK_HART3_ICACHE, 0x860)
+REG64(L2_WAY_MASK_HART4_DCACHE, 0x868)
+REG64(L2_WAY_MASK_HART4_ICACHE, 0x870)
+
+#define L2_CONFIG_RESET         0x06091004
+
+static uint64_t mchp_pfsoc_l2cc_way_enable_pre_write(RegisterInfo *reg,
+                                                     uint64_t value)
+{
+    uint64_t current = *(uint64_t *)reg->data;
+
+    return MAX(current, value);
+}
+
+#define WAY_MASK_REGISTER(_name)                \
+    {                                           \
+        .name = "WAY_MASK_" #_name,             \
+        .addr = A_L2_WAY_MASK_ ## _name,        \
+        .reset = UINT64_MAX,                    \
+    }
+
+static const RegisterAccessInfo mchp_pfsoc_l2cc_regs_info[] = {
+    {
+        .name = "CONFIG",
+        .addr = A_L2_CONFIG,
+        .reset = L2_CONFIG_RESET,
+        .ro = UINT64_MAX,
+    }, {
+        .name = "WAY_ENABLE",
+        .addr = A_L2_WAY_ENABLE,
+        .rsvd = ~R_L2_WAY_ENABLE_VALUE_MASK,
+        .pre_write = mchp_pfsoc_l2cc_way_enable_pre_write,
+    },
+    WAY_MASK_REGISTER(DMA),
+    WAY_MASK_REGISTER(AXI4_PORT_0),
+    WAY_MASK_REGISTER(AXI4_PORT_1),
+    WAY_MASK_REGISTER(AXI4_PORT_2),
+    WAY_MASK_REGISTER(AXI4_PORT_3),
+    WAY_MASK_REGISTER(HART0_DCACHE),
+    WAY_MASK_REGISTER(HART0_ICACHE),
+    WAY_MASK_REGISTER(HART1_DCACHE),
+    WAY_MASK_REGISTER(HART1_ICACHE),
+    WAY_MASK_REGISTER(HART2_DCACHE),
+    WAY_MASK_REGISTER(HART2_ICACHE),
+    WAY_MASK_REGISTER(HART3_DCACHE),
+    WAY_MASK_REGISTER(HART3_ICACHE),
+    WAY_MASK_REGISTER(HART4_DCACHE),
+    WAY_MASK_REGISTER(HART4_ICACHE),
+};
+
+static bool mchp_pfsoc_l2cc_register_implemented(hwaddr offset)
+{
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(mchp_pfsoc_l2cc_regs_info); i++) {
+        if (mchp_pfsoc_l2cc_regs_info[i].addr == offset) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static uint64_t mchp_pfsoc_l2cc_read(void *opaque, hwaddr offset,
+                                     unsigned size)
+{
+    if (mchp_pfsoc_l2cc_register_implemented(offset)) {
+        return register_read_memory(opaque, offset, size);
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read "
+                  "(size %u, offset 0x%" HWADDR_PRIx ")\n",
+                  __func__, size, offset);
+    return 0;
+}
+
+static void mchp_pfsoc_l2cc_write(void *opaque, hwaddr offset,
+                                  uint64_t value, unsigned size)
+{
+    if (mchp_pfsoc_l2cc_register_implemented(offset)) {
+        register_write_memory(opaque, offset, value, size);
+        return;
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write "
+                  "(size %u, value 0x%" PRIx64
+                  ", offset 0x%" HWADDR_PRIx ")\n",
+                  __func__, size, value, offset);
+}
+
+static const MemoryRegionOps mchp_pfsoc_l2cc_ops = {
+    .read = mchp_pfsoc_l2cc_read,
+    .write = mchp_pfsoc_l2cc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = { .min_access_size = 1, .max_access_size = 8 },
+    .impl = { .min_access_size = 1, .max_access_size = 8 },
+};
+
+static void mchp_pfsoc_l2cc_reset(DeviceState *dev)
+{
+    MchpPfSoCL2ccState *s = MCHP_PFSOC_L2CC(dev);
+    size_t i;
+
+    memset(s->regs, 0, sizeof(s->regs));
+    for (i = 0; i < ARRAY_SIZE(mchp_pfsoc_l2cc_regs_info); i++) {
+        hwaddr addr = mchp_pfsoc_l2cc_regs_info[i].addr;
+
+        register_reset(&s->regs_info[addr / sizeof(uint64_t)]);
+    }
+}
+
+static void mchp_pfsoc_l2cc_init(Object *obj)
+{
+    MchpPfSoCL2ccState *s = MCHP_PFSOC_L2CC(obj);
+    RegisterInfoArray *reg_array;
+
+    reg_array = register_init_block64(
+        DEVICE(obj), mchp_pfsoc_l2cc_regs_info,
+        ARRAY_SIZE(mchp_pfsoc_l2cc_regs_info), s->regs_info, s->regs,
+        &mchp_pfsoc_l2cc_ops, false, MCHP_PFSOC_L2CC_REG_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &reg_array->mem);
+}
+
+static void mchp_pfsoc_l2cc_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, mchp_pfsoc_l2cc_reset);
+}
+
+static const TypeInfo mchp_pfsoc_l2cc_info = {
+    .name = TYPE_MCHP_PFSOC_L2CC,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(MchpPfSoCL2ccState),
+    .instance_init = mchp_pfsoc_l2cc_init,
+    .class_init = mchp_pfsoc_l2cc_class_init,
+};
+
+static void mchp_pfsoc_l2cc_register_types(void)
+{
+    type_register_static(&mchp_pfsoc_l2cc_info);
+}
+
+type_init(mchp_pfsoc_l2cc_register_types)
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1543ee6653..772a2d87b3 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -203,6 +203,9 @@ config MCHP_PFSOC_DMC
 config MCHP_PFSOC_IOSCB
     bool
 
+config MCHP_PFSOC_L2CC
+    bool
+
 config MCHP_PFSOC_SYSREG
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 23265f6035..5f45861d8a 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -30,6 +30,7 @@ system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c'))
 # RISC-V devices
 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_L2CC', if_true: files('mchp_pfsoc_l2cc.c'))
 system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
-- 
2.34.1