@@ -1,3 +1,12 @@
+config CFAM
+ bool
+ select FSI
+ select SCRATCHPAD
+ select LBUS
+
+config FSI
+ bool
+
config SCRATCHPAD
bool
select LBUS
new file mode 100644
@@ -0,0 +1,241 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2023 IBM Corp.
+ *
+ * IBM Common FRU Access Macro
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/fsi/bits.h"
+#include "hw/fsi/cfam.h"
+#include "hw/fsi/engine-scratchpad.h"
+
+#include "hw/qdev-properties.h"
+
+#define TO_REG(x) ((x) >> 2)
+
+#define CFAM_ENGINE_CONFIG TO_REG(0x04)
+
+#define CFAM_CONFIG_CHIP_ID TO_REG(0x00)
+#define CFAM_CONFIG_CHIP_ID_P9 0xc0022d15
+#define CFAM_CONFIG_CHIP_ID_BREAK 0xc0de0000
+
+static uint64_t cfam_config_read(void *opaque, hwaddr addr, unsigned size)
+{
+ CFAMConfig *config;
+ CFAMState *cfam;
+ LBusNode *node;
+ int i;
+
+ config = CFAM_CONFIG(opaque);
+ cfam = container_of(config, CFAMState, config);
+
+ qemu_log_mask(LOG_UNIMP, "%s: read @0x%" HWADDR_PRIx " size=%d\n",
+ __func__, addr, size);
+
+ assert(size == 4);
+ assert(!(addr & 3));
+
+ switch (addr) {
+ case 0x00:
+ return CFAM_CONFIG_CHIP_ID_P9;
+ case 0x04:
+ return ENGINE_CONFIG_NEXT
+ | 0x00010000 /* slots */
+ | 0x00001000 /* version */
+ | ENGINE_CONFIG_TYPE_PEEK /* type */
+ | 0x0000000c; /* crc */
+ case 0x08:
+ return ENGINE_CONFIG_NEXT
+ | 0x00010000 /* slots */
+ | 0x00005000 /* version */
+ | ENGINE_CONFIG_TYPE_FSI /* type */
+ | 0x0000000a; /* crc */
+ break;
+ default:
+ /* FIXME: Improve this */
+ i = 0xc;
+ QLIST_FOREACH(node, &cfam->lbus.devices, next) {
+ if (i == addr) {
+ return LBUS_DEVICE_GET_CLASS(node->ldev)->config;
+ }
+ i += size;
+ }
+
+ if (i == addr) {
+ return 0;
+ }
+
+ /*
+ * As per FSI specification, This is a magic value at address 0 of
+ * given FSI port. This causes FSI master to send BREAK command for
+ * initialization and recovery.
+ */
+ return 0xc0de0000;
+ }
+}
+
+static void cfam_config_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ CFAMConfig *s = CFAM_CONFIG(opaque);
+
+ qemu_log_mask(LOG_UNIMP, "%s: write @0x%" HWADDR_PRIx " size=%d "
+ "value=%"PRIx64"\n", __func__, addr, size, data);
+
+ assert(size == 4);
+ assert(!(addr & 3));
+
+ switch (TO_REG(addr)) {
+ case CFAM_CONFIG_CHIP_ID:
+ case CFAM_CONFIG_CHIP_ID + 4:
+ if (data == CFAM_CONFIG_CHIP_ID_BREAK) {
+ bus_cold_reset(qdev_get_parent_bus(DEVICE(s)));
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Not implemented: 0x%"
+ HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ }
+}
+
+static const struct MemoryRegionOps cfam_config_ops = {
+ .read = cfam_config_read,
+ .write = cfam_config_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void cfam_config_realize(DeviceState *dev, Error **errp)
+{
+ CFAMConfig *s = CFAM_CONFIG(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &cfam_config_ops, s,
+ TYPE_CFAM_CONFIG, 0x400);
+}
+
+static void cfam_config_reset(DeviceState *dev)
+{
+ /* Config is read-only */
+}
+
+static void cfam_config_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->bus_type = TYPE_LBUS;
+ dc->realize = cfam_config_realize;
+ dc->reset = cfam_config_reset;
+}
+
+static const TypeInfo cfam_config_info = {
+ .name = TYPE_CFAM_CONFIG,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(CFAMConfig),
+ .class_init = cfam_config_class_init,
+};
+
+static uint64_t cfam_unimplemented_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: read @0x%" HWADDR_PRIx " size=%d\n",
+ __func__, addr, size);
+
+ return 0;
+}
+
+static void cfam_unimplemented_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: write @0x%" HWADDR_PRIx " size=%d "
+ "value=%"PRIx64"\n", __func__, addr, size, data);
+}
+
+static const struct MemoryRegionOps cfam_unimplemented_ops = {
+ .read = cfam_unimplemented_read,
+ .write = cfam_unimplemented_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void cfam_realize(DeviceState *dev, Error **errp)
+{
+ CFAMState *cfam = CFAM(dev);
+ FSISlaveState *slave = FSI_SLAVE(dev);
+ Error *err = NULL;
+
+ /* Each slave has a 2MiB address space */
+ memory_region_init_io(&cfam->mr, OBJECT(cfam), &cfam_unimplemented_ops,
+ cfam, TYPE_CFAM, 2 * 1024 * 1024);
+ address_space_init(&cfam->as, &cfam->mr, TYPE_CFAM);
+
+ qbus_init(&cfam->lbus, sizeof(cfam->lbus), TYPE_LBUS,
+ DEVICE(cfam), NULL);
+
+ lbus_create_device(&cfam->lbus, TYPE_SCRATCHPAD, 0);
+
+ object_property_set_bool(OBJECT(&cfam->config), "realized", true, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ qdev_set_parent_bus(DEVICE(&cfam->config), BUS(&cfam->lbus), &error_abort);
+
+ object_property_set_bool(OBJECT(&cfam->lbus), "realized", true, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&cfam->mr, 0, &cfam->config.iomem);
+ /* memory_region_add_subregion(&cfam->mr, 0x800, &cfam->lbus.peek.iomem); */
+ memory_region_add_subregion(&cfam->mr, 0x800, &slave->iomem);
+ memory_region_add_subregion(&cfam->mr, 0xc00, &cfam->lbus.mr);
+}
+
+static void cfam_reset(DeviceState *dev)
+{
+ /* TODO: Figure out how inherited reset works */
+ qemu_log_mask(LOG_UNIMP, "%s: Not implemented yet.\n", __func__);
+}
+
+static void cfam_init(Object *o)
+{
+ CFAMState *s = CFAM(o);
+
+ object_initialize_child(o, TYPE_CFAM_CONFIG, &s->config, TYPE_CFAM_CONFIG);
+}
+
+static void cfam_finalize(Object *o)
+{
+ CFAMState *s = CFAM(o);
+
+ address_space_destroy(&s->as);
+}
+
+static void cfam_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->bus_type = TYPE_FSI_BUS;
+ dc->realize = cfam_realize;
+ dc->reset = cfam_reset;
+}
+
+static const TypeInfo cfam_info = {
+ .name = TYPE_CFAM,
+ .parent = TYPE_FSI_SLAVE,
+ .instance_init = cfam_init,
+ .instance_finalize = cfam_finalize,
+ .instance_size = sizeof(CFAMState),
+ .class_init = cfam_class_init,
+};
+
+static void cfam_register_types(void)
+{
+ type_register_static(&cfam_config_info);
+ type_register_static(&cfam_info);
+}
+
+type_init(cfam_register_types);
new file mode 100644
@@ -0,0 +1,109 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2023 IBM Corp.
+ *
+ * IBM Flexible Service Interface slave
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/bitops.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/fsi/fsi-slave.h"
+
+#define TO_REG(x) ((x) >> 2)
+
+#define FSI_SMODE TO_REG(0x00)
+#define FSI_SMODE_WSTART BE_BIT(0)
+#define FSI_SMODE_AUX_EN BE_BIT(1)
+#define FSI_SMODE_SLAVE_ID BE_GENMASK(6, 7)
+#define FSI_SMODE_ECHO_DELAY BE_GENMASK(8, 11)
+#define FSI_SMODE_SEND_DELAY BE_GENMASK(12, 15)
+#define FSI_SMODE_LBUS_DIV BE_GENMASK(20, 23)
+#define FSI_SMODE_BRIEF_LEFT BE_GENMASK(24, 27)
+#define FSI_SMODE_BRIEF_RIGHT BE_GENMASK(28, 31)
+
+#define FSI_SDMA TO_REG(0x04)
+#define FSI_SISC TO_REG(0x08)
+#define FSI_SCISC TO_REG(0x08)
+#define FSI_SISM TO_REG(0x0c)
+#define FSI_SISS TO_REG(0x10)
+#define FSI_SSISM TO_REG(0x10)
+#define FSI_SCISM TO_REG(0x14)
+
+static uint64_t fsi_slave_read(void *opaque, hwaddr addr, unsigned size)
+{
+ FSISlaveState *s = FSI_SLAVE(opaque);
+
+ qemu_log_mask(LOG_UNIMP, "%s: read @0x%" HWADDR_PRIx " size=%d\n",
+ __func__, addr, size);
+
+ if (addr + size > sizeof(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out of bounds read: 0x%"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return 0;
+ }
+
+ return s->regs[TO_REG(addr)];
+}
+
+static void fsi_slave_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ FSISlaveState *s = FSI_SLAVE(opaque);
+
+ qemu_log_mask(LOG_UNIMP, "%s: write @0x%" HWADDR_PRIx " size=%d "
+ "value=%"PRIx64"\n", __func__, addr, size, data);
+
+ if (addr + size > sizeof(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out of bounds write: 0x%"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return;
+ }
+
+ s->regs[TO_REG(addr)] = data;
+}
+
+static const struct MemoryRegionOps fsi_slave_ops = {
+ .read = fsi_slave_read,
+ .write = fsi_slave_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void fsi_slave_init(Object *o)
+{
+ FSISlaveState *s = FSI_SLAVE(o);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &fsi_slave_ops,
+ s, TYPE_FSI_SLAVE, 0x400);
+}
+
+static void fsi_slave_reset(DeviceState *dev)
+{
+ /* FIXME */
+}
+
+static void fsi_slave_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->reset = fsi_slave_reset;
+}
+
+static const TypeInfo fsi_slave_info = {
+ .name = TYPE_FSI_SLAVE,
+ .parent = TYPE_DEVICE,
+ .instance_init = fsi_slave_init,
+ .instance_size = sizeof(FSISlaveState),
+ .class_init = fsi_slave_class_init,
+};
+
+static void fsi_slave_register_types(void)
+{
+ type_register_static(&fsi_slave_info);
+}
+
+type_init(fsi_slave_register_types);
@@ -1,2 +1,4 @@
system_ss.add(when: 'CONFIG_LBUS', if_true: files('lbus.c'))
system_ss.add(when: 'CONFIG_SCRATCHPAD', if_true: files('engine-scratchpad.c'))
+system_ss.add(when: 'CONFIG_CFAM', if_true: files('cfam.c'))
+system_ss.add(when: 'CONFIG_FSI', if_true: files('fsi-slave.c'))
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2023 IBM Corp.
+ *
+ * IBM Common FRU Access Macro
+ */
+#ifndef FSI_CFAM_H
+#define FSI_CFAM_H
+
+#include "exec/memory.h"
+
+#include "hw/fsi/fsi-slave.h"
+#include "hw/fsi/lbus.h"
+
+#define TYPE_FSI_BUS "fsi.bus"
+
+#define TYPE_CFAM "cfam"
+#define CFAM(obj) OBJECT_CHECK(CFAMState, (obj), TYPE_CFAM)
+
+#define CFAM_NR_REGS ((0x2e0 >> 2) + 1)
+
+#define TYPE_CFAM_CONFIG "cfam.config"
+#define CFAM_CONFIG(obj) \
+ OBJECT_CHECK(CFAMConfig, (obj), TYPE_CFAM_CONFIG)
+/* P9-ism */
+#define CFAM_CONFIG_NR_REGS 0x28
+
+typedef struct CFAMState CFAMState;
+
+/* TODO: Generalise this accommodate different CFAM configurations */
+typedef struct CFAMConfig {
+ DeviceState parent;
+
+ MemoryRegion iomem;
+} CFAMConfig;
+
+#define TYPE_CFAM_PEEK "cfam.peek"
+#define CFAM_PEEK(obj) \
+ OBJECT_CHECK(CFAMPeek, (obj), TYPE_CFAM_PEEK)
+#define CFAM_PEEK_NR_REGS ((0x130 >> 2) + 1)
+
+typedef struct CFAMPeek {
+ DeviceState parent;
+
+ MemoryRegion iomem;
+} CFAMPeek;
+
+struct CFAMState {
+ /* < private > */
+ FSISlaveState parent;
+
+ MemoryRegion mr;
+ AddressSpace as;
+
+ CFAMConfig config;
+ CFAMPeek peek;
+
+ LBus lbus;
+};
+
+#endif /* FSI_CFAM_H */
new file mode 100644
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2023 IBM Corp.
+ *
+ * IBM Flexible Service Interface slave
+ */
+#ifndef FSI_FSI_SLAVE_H
+#define FSI_FSI_SLAVE_H
+
+#include "exec/memory.h"
+#include "hw/qdev-core.h"
+
+#include "hw/fsi/lbus.h"
+
+#include <stdint.h>
+
+#define TYPE_FSI_SLAVE "fsi.slave"
+#define FSI_SLAVE(obj) \
+ OBJECT_CHECK(FSISlaveState, (obj), TYPE_FSI_SLAVE)
+#define FSI_SLAVE_CONTROL_NR_REGS ((0x40 >> 2) + 1)
+
+typedef struct FSISlaveState {
+ DeviceState parent;
+
+ MemoryRegion iomem;
+ uint32_t regs[FSI_SLAVE_CONTROL_NR_REGS];
+} FSISlaveState;
+
+#endif /* FSI_FSI_H */