[PATCH v2 2/8] fsi/cfam: Add CFAM-S model
Mikail Sadic <[email protected]> Thu, 30 Jul 2026 15:09:37 -0500
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
Add TYPE_FSI_CFAM_S, derivative of CFAM, as a second type on the common CFAM base class. The base class builds the register slot, the config table region, the FSI responder and the local bus, so the CFAM-S supplies only its config table (chip ID with major 9, an FSI responder and a v1 mailbox), its slot offsets and the mailbox engine. Its realize chains the base one and adds the one thing that is new: the slot is aliased across the four slave-ID views, so enumeration and normal access reach the same registers. The v1 mailbox is a local bus engine added in lbus.c next to the scratchpad which the CFAM-S just instantiates. Sharing the base config table implementation means the CFAM-S also honors the SID_BREAK sequence and word-only config access. FSIMasterState gains a cfam_s field. fsi_master_init() creates both the cfam and cfam-s children, realize hooks them into the OPB-to-FSI aperture. Signed-off-by: Mikail Sadic <[email protected]> --- docs/specs/fsi.rst | 15 ++++++ include/hw/fsi/cfam-s.h | 40 ++++++++++++++++ include/hw/fsi/cfam.h | 10 ++++ include/hw/fsi/fsi-master.h | 2 + include/hw/fsi/lbus.h | 11 +++++ hw/fsi/cfam-s.c | 91 +++++++++++++++++++++++++++++++++++++ hw/fsi/fsi-master.c | 7 +++ hw/fsi/lbus.c | 69 ++++++++++++++++++++++++++++ hw/fsi/meson.build | 2 +- hw/fsi/trace-events | 2 + 10 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 include/hw/fsi/cfam-s.h create mode 100644 hw/fsi/cfam-s.c diff --git a/docs/specs/fsi.rst b/docs/specs/fsi.rst index f7d86d3e37..fb7a3669ef 100644 --- a/docs/specs/fsi.rst +++ b/docs/specs/fsi.rst @@ -120,3 +120,18 @@ from the BMC. (see the `pdbg source repository`_ for more details) .. _pdbg source repository: https://github.com/open-power/pdbg + +CFAM-S model +------------ + +The CFAM-S is a derivative of the CFAM. Both are modelled on a common CFAM +model that builds the register slot, the configuration table, the FSI +responder and the local bus; each supplies its own configuration table, slot +layout and engines. The CFAM-S supports a limited set of engines: an FSI +responder and a version 1 mailbox. + +The configuration table advertises the responder and the mailbox engines. +The responder is backed by the FSI slave control registers; the mailbox +provides a small block of scratch registers. The register block is exposed +through each slave-ID view so that enumeration and normal access reach the +same registers. diff --git a/include/hw/fsi/cfam-s.h b/include/hw/fsi/cfam-s.h new file mode 100644 index 0000000000..daf72124c4 --- /dev/null +++ b/include/hw/fsi/cfam-s.h @@ -0,0 +1,40 @@ +/* + * IBM Common FRU Access Macro - S variant (CFAM-S) + * + * Copyright (C) 2026 IBM Corp. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef FSI_CFAM_S_H +#define FSI_CFAM_S_H + +#include "system/memory.h" +#include "hw/fsi/cfam.h" +#include "hw/fsi/fsi.h" +#include "hw/fsi/lbus.h" + +#define TYPE_FSI_CFAM_S "cfam-s" +OBJECT_DECLARE_TYPE(FSICFAMSState, FSICFAMSClass, FSI_CFAM_S) + +/* The register slot is visible in each of the four slave-ID views */ +#define CFAM_S_WINDOW_SIZE (4 * FSI_CFAM_SLOT_SIZE) + +struct FSICFAMSState { + /* < private > */ + FSICFAMCommonState parent; + + /* parent.mr aliased across the slave-ID views */ + MemoryRegion window; + MemoryRegion slot_alias[3]; + + FSIMbox mbox; +}; + +struct FSICFAMSClass { + /* < private > */ + FSICFAMCommonClass parent_class; + + DeviceRealize parent_realize; +}; + +#endif /* FSI_CFAM_S_H */ diff --git a/include/hw/fsi/cfam.h b/include/hw/fsi/cfam.h index 0f5464b4a0..43f6be9d18 100644 --- a/include/hw/fsi/cfam.h +++ b/include/hw/fsi/cfam.h @@ -35,6 +35,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(FSICFAMState, FSI_CFAM) #define ENGINE_CONFIG_TYPE_PEEK (0x02 << 4) #define ENGINE_CONFIG_TYPE_FSI (0x03 << 4) #define ENGINE_CONFIG_TYPE_SCRATCHPAD (0x06 << 4) +#define ENGINE_CONFIG_TYPE_MBOX_V1 (0x14 << 4) /* Valid, slots, version, type, crc */ #define CFAM_CONFIG_REG(__VER, __TYPE, __CRC) \ @@ -44,6 +45,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(FSICFAMState, FSI_CFAM) (__TYPE) | \ (__CRC)) +/* As above, for the last entry in a table: NEXT is clear */ +#define CFAM_CONFIG_LAST(__VER, __TYPE, __CRC) \ + (0x00010000 | \ + (__VER) | \ + (__TYPE) | \ + (__CRC)) + +#define CFAM_CONFIG_CHIP_ID_MAJOR(__MAJOR) (((__MAJOR) & 0xf) << 8) + struct FSICFAMCommonState { /* < private > */ FSISlaveState parent; diff --git a/include/hw/fsi/fsi-master.h b/include/hw/fsi/fsi-master.h index 60ddaa994f..6a2782fdf2 100644 --- a/include/hw/fsi/fsi-master.h +++ b/include/hw/fsi/fsi-master.h @@ -11,6 +11,7 @@ #include "hw/core/qdev.h" #include "hw/fsi/fsi.h" #include "hw/fsi/cfam.h" +#include "hw/fsi/cfam-s.h" #define TYPE_FSI_MASTER "fsi.master" OBJECT_DECLARE_SIMPLE_TYPE(FSIMasterState, FSI_MASTER) @@ -26,6 +27,7 @@ typedef struct FSIMasterState { uint32_t regs[FSI_MASTER_NR_REGS]; FSICFAMState cfam; + FSICFAMSState cfam_s; } FSIMasterState; diff --git a/include/hw/fsi/lbus.h b/include/hw/fsi/lbus.h index 1b894509fe..582d7169d6 100644 --- a/include/hw/fsi/lbus.h +++ b/include/hw/fsi/lbus.h @@ -40,4 +40,15 @@ typedef struct FSIScratchPad { uint32_t regs[FSI_SCRATCHPAD_NR_REGS]; } FSIScratchPad; +#define TYPE_FSI_MBOX "fsi.mbox" +OBJECT_DECLARE_SIMPLE_TYPE(FSIMbox, FSI_MBOX) + +#define FSI_MBOX_SCRATCH_NUM 5 + +struct FSIMbox { + FSILBusDevice parent; + + uint32_t scratch[FSI_MBOX_SCRATCH_NUM]; +}; + #endif /* FSI_LBUS_H */ diff --git a/hw/fsi/cfam-s.c b/hw/fsi/cfam-s.c new file mode 100644 index 0000000000..3df6da3788 --- /dev/null +++ b/hw/fsi/cfam-s.c @@ -0,0 +1,91 @@ +/* + * IBM Common FRU Access Macro - S variant (CFAM-S) + * + * A CFAM flavor built on the common CFAM model (see cfam.c). It supports an + * FSI responder and a v1 mailbox, and exposes its register slot in each + * slave-ID view of the link. + * + * Copyright (C) 2026 IBM Corp. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/units.h" +#include "hw/fsi/cfam-s.h" +#include "hw/fsi/fsi.h" + +/* Slot layout: 0x000 config table, 0x400 responder, 0x800 mailbox */ +#define CFAM_S_RESPONDER_BASE 0x400 +#define CFAM_S_MBOX_BASE 0x800 + +/* Config table: chip ID with major 9 (the CFAM-S), then the engines */ +static const uint32_t cfam_s_config[] = { + ENGINE_CONFIG_NEXT | CFAM_CONFIG_CHIP_ID_MAJOR(9) | 0xd, + CFAM_CONFIG_REG(0x1000, ENGINE_CONFIG_TYPE_FSI, 0xb), + CFAM_CONFIG_LAST(0x1000, ENGINE_CONFIG_TYPE_MBOX_V1, 0x3), +}; + +static bool fsi_cfam_s_realize_engines(FSICFAMCommonState *cfam, Error **errp) +{ + FSICFAMSState *cfam_s = FSI_CFAM_S(cfam); + + object_initialize_child(OBJECT(cfam_s), "mbox", &cfam_s->mbox, + TYPE_FSI_MBOX); + return fsi_cfam_add_engine(cfam, DEVICE(&cfam_s->mbox), 0, errp); +} + +static void fsi_cfam_s_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + FSICFAMSState *cfam_s = FSI_CFAM_S(dev); + FSICFAMCommonState *cfam = FSI_CFAM_COMMON(dev); + FSICFAMSClass *sc = FSI_CFAM_S_GET_CLASS(dev); + + sc->parent_realize(dev, errp); + if (*errp) { + return; + } + + memory_region_init(&cfam_s->window, OBJECT(cfam_s), + TYPE_FSI_CFAM_S ".window", CFAM_S_WINDOW_SIZE); + memory_region_add_subregion(&cfam_s->window, 0, &cfam->mr); + + /* Alias the slot into the other three slave-ID views */ + for (int i = 0; i < ARRAY_SIZE(cfam_s->slot_alias); i++) { + memory_region_init_alias(&cfam_s->slot_alias[i], OBJECT(cfam_s), + TYPE_FSI_CFAM_S ".slot-alias", &cfam->mr, 0, + FSI_CFAM_SLOT_SIZE); + memory_region_add_subregion(&cfam_s->window, + (i + 1) * FSI_CFAM_SLOT_SIZE, + &cfam_s->slot_alias[i]); + } +} + +static void fsi_cfam_s_class_init(ObjectClass *klass, const void *data) +{ + FSICFAMSClass *sc = FSI_CFAM_S_CLASS(klass); + FSICFAMCommonClass *cc = FSI_CFAM_COMMON_CLASS(klass); + + device_class_set_parent_realize(DEVICE_CLASS(klass), fsi_cfam_s_realize, + &sc->parent_realize); + + cc->config = cfam_s_config; + cc->config_nr = ARRAY_SIZE(cfam_s_config); + cc->responder_offset = CFAM_S_RESPONDER_BASE; + cc->lbus_offset = CFAM_S_MBOX_BASE; + cc->realize_engines = fsi_cfam_s_realize_engines; +} + +static const TypeInfo cfam_s_types[] = { + { + .name = TYPE_FSI_CFAM_S, + .parent = TYPE_FSI_CFAM_COMMON, + .instance_size = sizeof(FSICFAMSState), + .class_size = sizeof(FSICFAMSClass), + .class_init = fsi_cfam_s_class_init, + }, +}; + +DEFINE_TYPES(cfam_s_types) diff --git a/hw/fsi/fsi-master.c b/hw/fsi/fsi-master.c index d82df1c094..82991b1005 100644 --- a/hw/fsi/fsi-master.c +++ b/hw/fsi/fsi-master.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/log.h" +#include "qemu/units.h" #include "trace.h" #include "hw/fsi/fsi-master.h" @@ -113,6 +114,7 @@ static void fsi_master_init(Object *o) FSIMasterState *s = FSI_MASTER(o); object_initialize_child(o, "cfam", &s->cfam, TYPE_FSI_CFAM); + object_initialize_child(o, "cfam-s", &s->cfam_s, TYPE_FSI_CFAM_S); qbus_init(&s->bus, sizeof(s->bus), TYPE_FSI_BUS, DEVICE(s), NULL); @@ -131,6 +133,11 @@ static void fsi_master_realize(DeviceState *dev, Error **errp) /* address ? */ memory_region_add_subregion(&s->opb2fsi, 0, &s->cfam.parent.mr); + + if (!qdev_realize(DEVICE(&s->cfam_s), BUS(&s->bus), errp)) { + return; + } + memory_region_add_subregion(&s->opb2fsi, 2 * MiB, &s->cfam_s.window); } static void fsi_master_reset(DeviceState *dev) diff --git a/hw/fsi/lbus.c b/hw/fsi/lbus.c index cae29e0658..902f8d8a5e 100644 --- a/hw/fsi/lbus.c +++ b/hw/fsi/lbus.c @@ -107,11 +107,80 @@ static const TypeInfo fsi_scratchpad_info = { .class_init = fsi_scratchpad_class_init, }; +/* The mailbox exposes its scratch registers at this offset */ +#define FSI_MBOX_SCRATCH_OFF 0xe0 +#define FSI_MBOX_SCRATCH_END (FSI_MBOX_SCRATCH_OFF + FSI_MBOX_SCRATCH_NUM * 4) + +static uint64_t fsi_mbox_read(void *opaque, hwaddr addr, unsigned size) +{ + FSIMbox *mbox = FSI_MBOX(opaque); + + trace_fsi_mbox_read(addr, size); + + if (addr < FSI_MBOX_SCRATCH_OFF || addr >= FSI_MBOX_SCRATCH_END) { + return 0; + } + + return mbox->scratch[TO_REG(addr - FSI_MBOX_SCRATCH_OFF)]; +} + +static void fsi_mbox_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + FSIMbox *mbox = FSI_MBOX(opaque); + + trace_fsi_mbox_write(addr, size, data); + + if (addr < FSI_MBOX_SCRATCH_OFF || addr >= FSI_MBOX_SCRATCH_END) { + return; + } + + mbox->scratch[TO_REG(addr - FSI_MBOX_SCRATCH_OFF)] = data; +} + +static const struct MemoryRegionOps fsi_mbox_ops = { + .read = fsi_mbox_read, + .write = fsi_mbox_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void fsi_mbox_realize(DeviceState *dev, Error **errp) +{ + FSILBusDevice *ldev = FSI_LBUS_DEVICE(dev); + + memory_region_init_io(&ldev->iomem, OBJECT(ldev), &fsi_mbox_ops, + ldev, TYPE_FSI_MBOX, 0x400); +} + +static void fsi_mbox_reset(DeviceState *dev) +{ + FSIMbox *mbox = FSI_MBOX(dev); + + memset(mbox->scratch, 0, sizeof(mbox->scratch)); +} + +static void fsi_mbox_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->bus_type = TYPE_FSI_LBUS; + dc->realize = fsi_mbox_realize; + device_class_set_legacy_reset(dc, fsi_mbox_reset); +} + +static const TypeInfo fsi_mbox_info = { + .name = TYPE_FSI_MBOX, + .parent = TYPE_FSI_LBUS_DEVICE, + .instance_size = sizeof(FSIMbox), + .class_init = fsi_mbox_class_init, +}; + static void fsi_lbus_register_types(void) { type_register_static(&fsi_lbus_info); type_register_static(&fsi_lbus_device_type_info); type_register_static(&fsi_scratchpad_info); + type_register_static(&fsi_mbox_info); } type_init(fsi_lbus_register_types); diff --git a/hw/fsi/meson.build b/hw/fsi/meson.build index a18a076552..585d549405 100644 --- a/hw/fsi/meson.build +++ b/hw/fsi/meson.build @@ -1,2 +1,2 @@ -system_ss.add(when: 'CONFIG_FSI', if_true: files('lbus.c','fsi.c','cfam.c','fsi-master.c')) +system_ss.add(when: 'CONFIG_FSI', if_true: files('lbus.c','fsi.c','cfam.c','cfam-s.c','fsi-master.c')) system_ss.add(when: 'CONFIG_FSI_APB2OPB_ASPEED', if_true: files('aspeed_apb2opb.c')) diff --git a/hw/fsi/trace-events b/hw/fsi/trace-events index 9e286d08d3..42edce6437 100644 --- a/hw/fsi/trace-events +++ b/hw/fsi/trace-events @@ -1,5 +1,7 @@ fsi_scratchpad_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" fsi_scratchpad_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_mbox_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_mbox_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 fsi_slave_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" fsi_slave_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 fsi_cfam_config_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" -- 2.53.0