aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-02-01 14:42:11 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-02-01 14:42:11 +0000
commitc3709fde5955d13f6d4f86ab46ef3cc2288ca65e (patch)
treef9281d67609ab6c897f56d0c5006e93100e8656a /hw
parent89c958c120bcd10458d4cf2e0237cae31a14223b (diff)
parent649b8ed20543f1b7f7e3dd8fd409092639bb345e (diff)
downloadqemu-c3709fde5955d13f6d4f86ab46ef3cc2288ca65e.zip
qemu-c3709fde5955d13f6d4f86ab46ef3cc2288ca65e.tar.gz
qemu-c3709fde5955d13f6d4f86ab46ef3cc2288ca65e.tar.bz2
Merge tag 'pull-aspeed-20240201' of https://github.com/legoater/qemu into staging
aspeed queue: * Update of buildroot images to 2023.11 (6.6.3 kernel) * Check of the valid CPU type supported by aspeed machines * Simplified models for the IBM's FSI bus and the Aspeed controller bridge # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmW7Sa8ACgkQUaNDx8/7 # 7KG7mw/8DbMJY6aqgq5YANszzem1ktJphPCNxq081cbczCOUpCNX4aL+0/ANvxxD # lbJQB+SZeIRmuFbxYPhq68rtzB4vG7tsQpns4H33EPKT4vuzF70lq4fgptMiun3q # 1ZJ2LF3jonvQWdhbC17wzAQz0FFb4F7XOxz++UL4okPsgzsYItnd+TWs8q7+erRb # 84UwN+eBTBAl/FiNk679/tBTqAfCVGgQ7dzotr4f3tg5POvrGOrlEjAn0O+dGGDj # wgILmpEBsTsilRB1tz8Kw0j/v/VkHz1DJu45lRAV9CIrN22iKcjMilNGgNDT8kcI # yAlxAw3iN+hVFqDov8wFPjDYd/Qw2oRAPy2Kd14hW9xL8zBOTms1JK5L0PS2+Feo # ZjMJ2cOJq3t4Wt1ZXRhgHfF4ANwP0OZ/y9bHCy3CkBljEeiTQbikHP9gVV4qHXZH # 4Q0HnDZQwAgobw3CmZ8jVx1dQueqy3ycuvkhCyv3S0l/tdbtXDtr5pNNu3dAP/PJ # 3nifLdRImhDvxxO9GKaCdUVLzELzMJl0GrgAsVJPKVnKHA4IiVKmB+XcW9IUbfy/ # 3zA2wHJLrEF+MF6MsuNcEYCCqUvyNLm7rUrXk1wNLXpCJ35bbW5IYy7Ty/8E2GHb # D5Cv/EPNhMBiNA4+HqQlMOTC13Ozv2qwCuWYCh2Ik8mnzaEiyTo= # =0C5S # -----END PGP SIGNATURE----- # gpg: Signature made Thu 01 Feb 2024 07:35:11 GMT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-aspeed-20240201' of https://github.com/legoater/qemu: hw/fsi: Update MAINTAINER list hw/fsi: Added FSI documentation hw/fsi: Added qtest hw/arm: Hook up FSI module in AST2600 hw/fsi: Aspeed APB2OPB & On-chip peripheral bus hw/fsi: Introduce IBM's FSI master hw/fsi: Introduce IBM's cfam hw/fsi: Introduce IBM's fsi-slave model hw/fsi: Introduce IBM's FSI Bus hw/fsi: Introduce IBM's scratchpad device hw/fsi: Introduce IBM's Local bus hw/arm/aspeed: Check for CPU types in machine_run_board_init() hw/arm/aspeed: Introduce aspeed_soc_cpu_type() helper hw/arm/aspeed: Init CPU defaults in a common helper hw/arm/aspeed: Set default CPU count using aspeed_soc_num_cpus() hw/arm/aspeed: Remove dead code tests/avocado/machine_aspeed.py: Update buildroot images to 2023.11 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/Kconfig1
-rw-r--r--hw/arm/Kconfig1
-rw-r--r--hw/arm/aspeed.c70
-rw-r--r--hw/arm/aspeed_ast10x0.c8
-rw-r--r--hw/arm/aspeed_ast2400.c15
-rw-r--r--hw/arm/aspeed_ast2600.c28
-rw-r--r--hw/arm/aspeed_soc_common.c8
-rw-r--r--hw/fsi/Kconfig7
-rw-r--r--hw/fsi/aspeed_apb2opb.c367
-rw-r--r--hw/fsi/cfam.c168
-rw-r--r--hw/fsi/fsi-master.c170
-rw-r--r--hw/fsi/fsi.c102
-rw-r--r--hw/fsi/lbus.c117
-rw-r--r--hw/fsi/meson.build2
-rw-r--r--hw/fsi/trace-events13
-rw-r--r--hw/fsi/trace.h1
-rw-r--r--hw/meson.build1
17 files changed, 1031 insertions, 48 deletions
diff --git a/hw/Kconfig b/hw/Kconfig
index 9ca7b38..2c00936 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -9,6 +9,7 @@ source core/Kconfig
source cxl/Kconfig
source display/Kconfig
source dma/Kconfig
+source fsi/Kconfig
source gpio/Kconfig
source hyperv/Kconfig
source i2c/Kconfig
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index f927878..db08a00 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -580,6 +580,7 @@ config ASPEED_SOC
select LED
select PMBUS
select MAX31785
+ select FSI_APB2OPB_ASPEED
config MPS2
bool
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index cc59176..fc8355c 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -1141,10 +1141,15 @@ static void aspeed_machine_class_props_init(ObjectClass *oc)
"Change the SPI Flash model");
}
-static int aspeed_soc_num_cpus(const char *soc_name)
+static void aspeed_machine_class_init_cpus_defaults(MachineClass *mc)
{
- AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(soc_name));
- return sc->num_cpus;
+ AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(mc);
+ AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name));
+
+ mc->default_cpus = sc->num_cpus;
+ mc->min_cpus = sc->num_cpus;
+ mc->max_cpus = sc->num_cpus;
+ mc->valid_cpu_types = sc->valid_cpu_types;
}
static void aspeed_machine_class_init(ObjectClass *oc, void *data)
@@ -1176,8 +1181,7 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data)
amc->num_cs = 1;
amc->i2c_init = palmetto_bmc_i2c_init;
mc->default_ram_size = 256 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data)
@@ -1193,8 +1197,7 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data)
amc->num_cs = 1;
amc->i2c_init = quanta_q71l_bmc_i2c_init;
mc->default_ram_size = 128 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
}
static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc,
@@ -1212,6 +1215,7 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc,
amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON;
amc->i2c_init = palmetto_bmc_i2c_init;
mc->default_ram_size = 256 * MiB;
+ aspeed_machine_class_init_cpus_defaults(mc);
}
static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc,
@@ -1229,8 +1233,7 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc,
amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON;
amc->i2c_init = palmetto_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
}
static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data)
@@ -1246,8 +1249,7 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data)
amc->num_cs = 1;
amc->i2c_init = ast2500_evb_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data)
@@ -1264,8 +1266,7 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data)
amc->num_cs = 2;
amc->i2c_init = yosemitev2_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data)
@@ -1281,8 +1282,7 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data)
amc->num_cs = 2;
amc->i2c_init = romulus_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data)
@@ -1299,9 +1299,7 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data)
amc->num_cs = 2;
amc->i2c_init = tiogapass_bmc_i2c_init;
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data)
@@ -1317,8 +1315,7 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data)
amc->num_cs = 2;
amc->i2c_init = sonorapass_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data)
@@ -1334,8 +1331,7 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data)
amc->num_cs = 2;
amc->i2c_init = witherspoon_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data)
@@ -1354,8 +1350,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data)
ASPEED_MAC3_ON;
amc->i2c_init = ast2600_evb_i2c_init;
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data)
@@ -1373,8 +1368,7 @@ static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data)
amc->macs_mask = ASPEED_MAC2_ON;
amc->i2c_init = witherspoon_bmc_i2c_init; /* Same board layout */
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data)
@@ -1391,8 +1385,7 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data)
amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON;
amc->i2c_init = g220a_bmc_i2c_init;
mc->default_ram_size = 1024 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data)
@@ -1409,8 +1402,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data)
amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON;
amc->i2c_init = fp5280g2_bmc_i2c_init;
mc->default_ram_size = 512 * MiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data)
@@ -1428,8 +1420,7 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data)
amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON;
amc->i2c_init = rainier_bmc_i2c_init;
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
#define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
@@ -1450,8 +1441,7 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data)
amc->i2c_init = fuji_bmc_i2c_init;
amc->uart_default = ASPEED_DEV_UART1;
mc->default_ram_size = FUJI_BMC_RAM_SIZE;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
#define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
@@ -1471,8 +1461,7 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data)
amc->macs_mask = ASPEED_MAC2_ON;
amc->i2c_init = bletchley_bmc_i2c_init;
mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
}
static void fby35_reset(MachineState *state, ShutdownCause reason)
@@ -1514,6 +1503,7 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data)
amc->i2c_init = fby35_i2c_init;
/* FIXME: Replace this macro with something more general */
mc->default_ram_size = FUJI_BMC_RAM_SIZE;
+ aspeed_machine_class_init_cpus_defaults(mc);
}
#define AST1030_INTERNAL_FLASH_SIZE (1024 * 1024)
@@ -1587,11 +1577,11 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc,
mc->init = aspeed_minibmc_machine_init;
amc->i2c_init = ast1030_evb_i2c_init;
mc->default_ram_size = 0;
- mc->default_cpus = mc->min_cpus = mc->max_cpus = 1;
amc->fmc_model = "sst25vf032b";
amc->spi_model = "sst25vf032b";
amc->num_cs = 2;
amc->macs_mask = 0;
+ aspeed_machine_class_init_cpus_defaults(mc);
}
static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc,
@@ -1610,8 +1600,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc,
amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON;
amc->i2c_init = qcom_dc_scm_bmc_i2c_init;
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc,
@@ -1630,8 +1619,7 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc,
amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON;
amc->i2c_init = qcom_dc_scm_firework_i2c_init;
mc->default_ram_size = 1 * GiB;
- mc->default_cpus = mc->min_cpus = mc->max_cpus =
- aspeed_soc_num_cpus(amc->soc_name);
+ aspeed_machine_class_init_cpus_defaults(mc);
};
static const TypeInfo aspeed_machine_types[] = {
diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c
index 8becb14..c3b5116 100644
--- a/hw/arm/aspeed_ast10x0.c
+++ b/hw/arm/aspeed_ast10x0.c
@@ -211,7 +211,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp)
/* AST1030 CPU Core */
armv7m = DEVICE(&a->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 256);
- qdev_prop_set_string(armv7m, "cpu-type", sc->cpu_type);
+ qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc));
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
object_property_set_link(OBJECT(&a->armv7m), "memory",
OBJECT(s->memory), &error_abort);
@@ -417,13 +417,17 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp)
static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data)
{
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */
+ NULL
+ };
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc);
dc->realize = aspeed_soc_ast1030_realize;
sc->name = "ast1030-a1";
- sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); /* TODO cortex-m4f */
+ sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST1030_A1_SILICON_REV;
sc->sram_size = 0xc0000;
sc->secsram_size = 0x40000; /* 256 * KiB */
diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c
index ad76035..8829561 100644
--- a/hw/arm/aspeed_ast2400.c
+++ b/hw/arm/aspeed_ast2400.c
@@ -156,7 +156,8 @@ static void aspeed_ast2400_soc_init(Object *obj)
}
for (i = 0; i < sc->num_cpus; i++) {
- object_initialize_child(obj, "cpu[*]", &a->cpu[i], sc->cpu_type);
+ object_initialize_child(obj, "cpu[*]", &a->cpu[i],
+ aspeed_soc_cpu_type(sc));
}
snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname);
@@ -502,6 +503,10 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp)
static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data)
{
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("arm926"),
+ NULL
+ };
AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
@@ -510,7 +515,7 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data)
dc->user_creatable = false;
sc->name = "ast2400-a1";
- sc->cpu_type = ARM_CPU_TYPE_NAME("arm926");
+ sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST2400_A1_SILICON_REV;
sc->sram_size = 0x8000;
sc->spis_num = 1;
@@ -526,6 +531,10 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data)
static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data)
{
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("arm1176"),
+ NULL
+ };
AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
@@ -534,7 +543,7 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data)
dc->user_creatable = false;
sc->name = "ast2500-a1";
- sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176");
+ sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST2500_A1_SILICON_REV;
sc->sram_size = 0x9000;
sc->spis_num = 2;
diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
index 386a88d..4ee32ea 100644
--- a/hw/arm/aspeed_ast2600.c
+++ b/hw/arm/aspeed_ast2600.c
@@ -76,6 +76,8 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
[ASPEED_DEV_UART12] = 0x1E790600,
[ASPEED_DEV_UART13] = 0x1E790700,
[ASPEED_DEV_VUART] = 0x1E787000,
+ [ASPEED_DEV_FSI1] = 0x1E79B000,
+ [ASPEED_DEV_FSI2] = 0x1E79B100,
[ASPEED_DEV_I3C] = 0x1E7A0000,
[ASPEED_DEV_SDRAM] = 0x80000000,
};
@@ -133,6 +135,8 @@ static const int aspeed_soc_ast2600_irqmap[] = {
[ASPEED_DEV_ETH4] = 33,
[ASPEED_DEV_KCS] = 138, /* 138 -> 142 */
[ASPEED_DEV_DP] = 62,
+ [ASPEED_DEV_FSI1] = 100,
+ [ASPEED_DEV_FSI2] = 101,
[ASPEED_DEV_I3C] = 102, /* 102 -> 107 */
};
@@ -158,7 +162,8 @@ static void aspeed_soc_ast2600_init(Object *obj)
}
for (i = 0; i < sc->num_cpus; i++) {
- object_initialize_child(obj, "cpu[*]", &a->cpu[i], sc->cpu_type);
+ object_initialize_child(obj, "cpu[*]", &a->cpu[i],
+ aspeed_soc_cpu_type(sc));
}
snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname);
@@ -265,6 +270,10 @@ static void aspeed_soc_ast2600_init(Object *obj)
object_initialize_child(obj, "emmc-boot-controller",
&s->emmc_boot_controller,
TYPE_UNIMPLEMENTED_DEVICE);
+
+ for (i = 0; i < ASPEED_FSI_NUM; i++) {
+ object_initialize_child(obj, "fsi[*]", &s->fsi[i], TYPE_ASPEED_APB2OPB);
+ }
}
/*
@@ -624,17 +633,32 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
return;
}
aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]);
+
+ /* FSI */
+ for (i = 0; i < ASPEED_FSI_NUM; i++) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsi[i]), errp)) {
+ return;
+ }
+ aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fsi[i]), 0,
+ sc->memmap[ASPEED_DEV_FSI1 + i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fsi[i]), 0,
+ aspeed_soc_get_irq(s, ASPEED_DEV_FSI1 + i));
+ }
}
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
{
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("cortex-a7"),
+ NULL
+ };
DeviceClass *dc = DEVICE_CLASS(oc);
AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
dc->realize = aspeed_soc_ast2600_realize;
sc->name = "ast2600-a3";
- sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
+ sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST2600_A3_SILICON_REV;
sc->sram_size = 0x16400;
sc->spis_num = 2;
diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c
index 828f610..123a0c4 100644
--- a/hw/arm/aspeed_soc_common.c
+++ b/hw/arm/aspeed_soc_common.c
@@ -18,6 +18,14 @@
#include "hw/char/serial.h"
+const char *aspeed_soc_cpu_type(AspeedSoCClass *sc)
+{
+ assert(sc->valid_cpu_types);
+ assert(sc->valid_cpu_types[0]);
+ assert(!sc->valid_cpu_types[1]);
+ return sc->valid_cpu_types[0];
+}
+
qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev)
{
return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev);
diff --git a/hw/fsi/Kconfig b/hw/fsi/Kconfig
new file mode 100644
index 0000000..9cee657
--- /dev/null
+++ b/hw/fsi/Kconfig
@@ -0,0 +1,7 @@
+config FSI_APB2OPB_ASPEED
+ bool
+ depends on ASPEED_SOC
+ select FSI
+
+config FSI
+ bool
diff --git a/hw/fsi/aspeed_apb2opb.c b/hw/fsi/aspeed_apb2opb.c
new file mode 100644
index 0000000..ea50718
--- /dev/null
+++ b/hw/fsi/aspeed_apb2opb.c
@@ -0,0 +1,367 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 IBM Corp.
+ *
+ * ASPEED APB-OPB FSI interface
+ * IBM On-chip Peripheral Bus
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#include "hw/fsi/aspeed_apb2opb.h"
+#include "hw/qdev-core.h"
+
+#define TO_REG(x) (x >> 2)
+
+#define APB2OPB_VERSION TO_REG(0x00)
+#define APB2OPB_TRIGGER TO_REG(0x04)
+
+#define APB2OPB_CONTROL TO_REG(0x08)
+#define APB2OPB_CONTROL_OFF BE_GENMASK(31, 13)
+
+#define APB2OPB_OPB2FSI TO_REG(0x0c)
+#define APB2OPB_OPB2FSI_OFF BE_GENMASK(31, 22)
+
+#define APB2OPB_OPB0_SEL TO_REG(0x10)
+#define APB2OPB_OPB1_SEL TO_REG(0x28)
+#define APB2OPB_OPB_SEL_EN BIT(0)
+
+#define APB2OPB_OPB0_MODE TO_REG(0x14)
+#define APB2OPB_OPB1_MODE TO_REG(0x2c)
+#define APB2OPB_OPB_MODE_RD BIT(0)
+
+#define APB2OPB_OPB0_XFER TO_REG(0x18)
+#define APB2OPB_OPB1_XFER TO_REG(0x30)
+#define APB2OPB_OPB_XFER_FULL BIT(1)
+#define APB2OPB_OPB_XFER_HALF BIT(0)
+
+#define APB2OPB_OPB0_ADDR TO_REG(0x1c)
+#define APB2OPB_OPB0_WRITE_DATA TO_REG(0x20)
+
+#define APB2OPB_OPB1_ADDR TO_REG(0x34)
+#define APB2OPB_OPB1_WRITE_DATA TO_REG(0x38)
+
+#define APB2OPB_IRQ_STS TO_REG(0x48)
+#define APB2OPB_IRQ_STS_OPB1_TX_ACK BIT(17)
+#define APB2OPB_IRQ_STS_OPB0_TX_ACK BIT(16)
+
+#define APB2OPB_OPB0_WRITE_WORD_ENDIAN TO_REG(0x4c)
+#define APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE 0x0011101b
+#define APB2OPB_OPB0_WRITE_BYTE_ENDIAN TO_REG(0x50)
+#define APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE 0x0c330f3f
+#define APB2OPB_OPB1_WRITE_WORD_ENDIAN TO_REG(0x54)
+#define APB2OPB_OPB1_WRITE_BYTE_ENDIAN TO_REG(0x58)
+#define APB2OPB_OPB0_READ_BYTE_ENDIAN TO_REG(0x5c)
+#define APB2OPB_OPB1_READ_BYTE_ENDIAN TO_REG(0x60)
+#define APB2OPB_OPB0_READ_WORD_ENDIAN_BE 0x00030b1b
+
+#define APB2OPB_OPB0_READ_DATA TO_REG(0x84)
+#define APB2OPB_OPB1_READ_DATA TO_REG(0x90)
+
+/*
+ * The following magic values came from AST2600 data sheet
+ * The register values are defined under section "FSI controller"
+ * as initial values.
+ */
+static const uint32_t aspeed_apb2opb_reset[ASPEED_APB2OPB_NR_REGS] = {
+ [APB2OPB_VERSION] = 0x000000a1,
+ [APB2OPB_OPB0_WRITE_WORD_ENDIAN] = 0x0044eee4,
+ [APB2OPB_OPB0_WRITE_BYTE_ENDIAN] = 0x0055aaff,
+ [APB2OPB_OPB1_WRITE_WORD_ENDIAN] = 0x00117717,
+ [APB2OPB_OPB1_WRITE_BYTE_ENDIAN] = 0xffaa5500,
+ [APB2OPB_OPB0_READ_BYTE_ENDIAN] = 0x0044eee4,
+ [APB2OPB_OPB1_READ_BYTE_ENDIAN] = 0x00117717
+};
+
+static void fsi_opb_fsi_master_address(FSIMasterState *fsi, hwaddr addr)
+{
+ memory_region_transaction_begin();
+ memory_region_set_address(&fsi->iomem, addr);
+ memory_region_transaction_commit();
+}
+
+static void fsi_opb_opb2fsi_address(FSIMasterState *fsi, hwaddr addr)
+{
+ memory_region_transaction_begin();
+ memory_region_set_address(&fsi->opb2fsi, addr);
+ memory_region_transaction_commit();
+}
+
+static uint64_t fsi_aspeed_apb2opb_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ AspeedAPB2OPBState *s = ASPEED_APB2OPB(opaque);
+ unsigned int reg = TO_REG(addr);
+
+ trace_fsi_aspeed_apb2opb_read(addr, size);
+
+ if (reg >= ASPEED_APB2OPB_NR_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[reg];
+}
+
+static MemTxResult fsi_aspeed_apb2opb_rw(AddressSpace *as, hwaddr addr,
+ MemTxAttrs attrs, uint32_t *data,
+ uint32_t size, bool is_write)
+{
+ MemTxResult res;
+
+ if (is_write) {
+ switch (size) {
+ case 4:
+ address_space_stl_le(as, addr, *data, attrs, &res);
+ break;
+ case 2:
+ address_space_stw_le(as, addr, *data, attrs, &res);
+ break;
+ case 1:
+ address_space_stb(as, addr, *data, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ switch (size) {
+ case 4:
+ *data = address_space_ldl_le(as, addr, attrs, &res);
+ break;
+ case 2:
+ *data = address_space_lduw_le(as, addr, attrs, &res);
+ break;
+ case 1:
+ *data = address_space_ldub(as, addr, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+ return res;
+}
+
+static void fsi_aspeed_apb2opb_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ AspeedAPB2OPBState *s = ASPEED_APB2OPB(opaque);
+ unsigned int reg = TO_REG(addr);
+
+ trace_fsi_aspeed_apb2opb_write(addr, size, data);
+
+ if (reg >= ASPEED_APB2OPB_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out of bounds write: %"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return;
+ }
+
+ switch (reg) {
+ case APB2OPB_CONTROL:
+ fsi_opb_fsi_master_address(&s->fsi[0],
+ data & APB2OPB_CONTROL_OFF);
+ break;
+ case APB2OPB_OPB2FSI:
+ fsi_opb_opb2fsi_address(&s->fsi[0],
+ data & APB2OPB_OPB2FSI_OFF);
+ break;
+ case APB2OPB_OPB0_WRITE_WORD_ENDIAN:
+ if (data != APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bridge needs to be driven as BE (0x%x)\n",
+ __func__, APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE);
+ }
+ break;
+ case APB2OPB_OPB0_WRITE_BYTE_ENDIAN:
+ if (data != APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bridge needs to be driven as BE (0x%x)\n",
+ __func__, APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE);
+ }
+ break;
+ case APB2OPB_OPB0_READ_BYTE_ENDIAN:
+ if (data != APB2OPB_OPB0_READ_WORD_ENDIAN_BE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bridge needs to be driven as BE (0x%x)\n",
+ __func__, APB2OPB_OPB0_READ_WORD_ENDIAN_BE);
+ }
+ break;
+ case APB2OPB_TRIGGER:
+ {
+ uint32_t opb, op_mode, op_size, op_addr, op_data;
+ MemTxResult result;
+ bool is_write;
+ int index;
+ AddressSpace *as;
+
+ assert((s->regs[APB2OPB_OPB0_SEL] & APB2OPB_OPB_SEL_EN) ^
+ (s->regs[APB2OPB_OPB1_SEL] & APB2OPB_OPB_SEL_EN));
+
+ if (s->regs[APB2OPB_OPB0_SEL] & APB2OPB_OPB_SEL_EN) {
+ opb = 0;
+ op_mode = s->regs[APB2OPB_OPB0_MODE];
+ op_size = s->regs[APB2OPB_OPB0_XFER];
+ op_addr = s->regs[APB2OPB_OPB0_ADDR];
+ op_data = s->regs[APB2OPB_OPB0_WRITE_DATA];
+ } else if (s->regs[APB2OPB_OPB1_SEL] & APB2OPB_OPB_SEL_EN) {
+ opb = 1;
+ op_mode = s->regs[APB2OPB_OPB1_MODE];
+ op_size = s->regs[APB2OPB_OPB1_XFER];
+ op_addr = s->regs[APB2OPB_OPB1_ADDR];
+ op_data = s->regs[APB2OPB_OPB1_WRITE_DATA];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid operation: 0x%"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return;
+ }
+
+ if (op_size & ~(APB2OPB_OPB_XFER_HALF | APB2OPB_OPB_XFER_FULL)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OPB transaction failed: Unrecognized access width: %d\n",
+ op_size);
+ return;
+ }
+
+ op_size += 1;
+ is_write = !(op_mode & APB2OPB_OPB_MODE_RD);
+ index = opb ? APB2OPB_OPB1_READ_DATA : APB2OPB_OPB0_READ_DATA;
+ as = &s->opb[opb].as;
+
+ result = fsi_aspeed_apb2opb_rw(as, op_addr, MEMTXATTRS_UNSPECIFIED,
+ &op_data, op_size, is_write);
+ if (result != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: OPB %s failed @%08x\n",
+ __func__, is_write ? "write" : "read", op_addr);
+ return;
+ }
+
+ if (!is_write) {
+ s->regs[index] = op_data;
+ }
+
+ s->regs[APB2OPB_IRQ_STS] |= opb ? APB2OPB_IRQ_STS_OPB1_TX_ACK
+ : APB2OPB_IRQ_STS_OPB0_TX_ACK;
+ break;
+ }
+ }
+
+ s->regs[reg] = data;
+}
+
+static const struct MemoryRegionOps aspeed_apb2opb_ops = {
+ .read = fsi_aspeed_apb2opb_read,
+ .write = fsi_aspeed_apb2opb_write,
+ .valid.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void fsi_aspeed_apb2opb_init(Object *o)
+{
+ AspeedAPB2OPBState *s = ASPEED_APB2OPB(o);
+ int i;
+
+ for (i = 0; i < ASPEED_FSI_NUM; i++) {
+ object_initialize_child(o, "fsi-master[*]", &s->fsi[i],
+ TYPE_FSI_MASTER);
+ }
+}
+
+static void fsi_aspeed_apb2opb_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedAPB2OPBState *s = ASPEED_APB2OPB(dev);
+ int i;
+
+ /*
+ * TODO: The OPBus model initializes the OPB address space in
+ * the .instance_init handler and this is problematic for test
+ * device-introspect-test. To avoid a memory corruption and a QEMU
+ * crash, qbus_init() should be called from realize(). Something to
+ * improve. Possibly, OPBus could also be removed.
+ */
+ for (i = 0; i < ASPEED_FSI_NUM; i++) {
+ qbus_init(&s->opb[i], sizeof(s->opb[i]), TYPE_OP_BUS, DEVICE(s),
+ NULL);
+ }
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_apb2opb_ops, s,
+ TYPE_ASPEED_APB2OPB, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ for (i = 0; i < ASPEED_FSI_NUM; i++) {
+ if (!qdev_realize(DEVICE(&s->fsi[i]), BUS(&s->opb[i]), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->opb[i].mr, 0x80000000,
+ &s->fsi[i].iomem);
+
+ memory_region_add_subregion(&s->opb[i].mr, 0xa0000000,
+ &s->fsi[i].opb2fsi);
+ }
+}
+
+static void fsi_aspeed_apb2opb_reset(DeviceState *dev)
+{
+ AspeedAPB2OPBState *s = ASPEED_APB2OPB(dev);
+
+ memcpy(s->regs, aspeed_apb2opb_reset, ASPEED_APB2OPB_NR_REGS);
+}
+
+static void fsi_aspeed_apb2opb_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "ASPEED APB2OPB Bridge";
+ dc->realize = fsi_aspeed_apb2opb_realize;
+ dc->reset = fsi_aspeed_apb2opb_reset;
+}
+
+static const TypeInfo aspeed_apb2opb_info = {
+ .name = TYPE_ASPEED_APB2OPB,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = fsi_aspeed_apb2opb_init,
+ .instance_size = sizeof(AspeedAPB2OPBState),
+ .class_init = fsi_aspeed_apb2opb_class_init,
+};
+
+static void aspeed_apb2opb_register_types(void)
+{
+ type_register_static(&aspeed_apb2opb_info);
+}
+
+type_init(aspeed_apb2opb_register_types);
+
+static void fsi_opb_init(Object *o)
+{
+ OPBus *opb = OP_BUS(o);
+
+ memory_region_init(&opb->mr, 0, TYPE_FSI_OPB, UINT32_MAX);
+ address_space_init(&opb->as, &opb->mr, TYPE_FSI_OPB);
+}
+
+static const TypeInfo opb_info = {
+ .name = TYPE_OP_BUS,
+ .parent = TYPE_BUS,
+ .instance_init = fsi_opb_init,
+ .instance_size = sizeof(OPBus),
+};
+
+static void fsi_opb_register_types(void)
+{
+ type_register_static(&opb_info);
+}
+
+type_init(fsi_opb_register_types);
diff --git a/hw/fsi/cfam.c b/hw/fsi/cfam.c
new file mode 100644
index 0000000..c62f0f7
--- /dev/null
+++ b/hw/fsi/cfam.c
@@ -0,0 +1,168 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 IBM Corp.
+ *
+ * IBM Common FRU Access Macro
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+
+#include "qapi/error.h"
+#include "trace.h"
+
+#include "hw/fsi/cfam.h"
+#include "hw/fsi/fsi.h"
+
+#include "hw/qdev-properties.h"
+
+#define ENGINE_CONFIG_NEXT BIT(31)
+#define ENGINE_CONFIG_TYPE_PEEK (0x02 << 4)
+#define ENGINE_CONFIG_TYPE_FSI (0x03 << 4)
+#define ENGINE_CONFIG_TYPE_SCRATCHPAD (0x06 << 4)
+
+/* Valid, slots, version, type, crc */
+#define CFAM_CONFIG_REG(__VER, __TYPE, __CRC) \
+ (ENGINE_CONFIG_NEXT | \
+ 0x00010000 | \
+ (__VER) | \
+ (__TYPE) | \
+ (__CRC))
+
+#define TO_REG(x) ((x) >> 2)
+
+#define CFAM_CONFIG_CHIP_ID TO_REG(0x00)
+#define CFAM_CONFIG_PEEK_STATUS TO_REG(0x04)
+#define CFAM_CONFIG_CHIP_ID_P9 0xc0022d15
+#define CFAM_CONFIG_CHIP_ID_BREAK 0xc0de0000
+
+static uint64_t fsi_cfam_config_read(void *opaque, hwaddr addr, unsigned size)
+{
+ trace_fsi_cfam_config_read(addr, size);
+
+ switch (addr) {
+ case 0x00:
+ return CFAM_CONFIG_CHIP_ID_P9;
+ case 0x04:
+ return CFAM_CONFIG_REG(0x1000, ENGINE_CONFIG_TYPE_PEEK, 0xc);
+ case 0x08:
+ return CFAM_CONFIG_REG(0x5000, ENGINE_CONFIG_TYPE_FSI, 0xa);
+ case 0xc:
+ return CFAM_CONFIG_REG(0x1000, ENGINE_CONFIG_TYPE_SCRATCHPAD, 0x7);
+ default:
+ /*
+ * The config table contains different engines from 0xc onwards.
+ * The scratch pad is already added at address 0xc. We need to add
+ * future engines from address 0x10 onwards. Returning 0 as engine
+ * is not implemented.
+ */
+ return 0;
+ }
+}
+
+static void fsi_cfam_config_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ FSICFAMState *cfam = FSI_CFAM(opaque);
+
+ trace_fsi_cfam_config_write(addr, size, data);
+
+ switch (TO_REG(addr)) {
+ case CFAM_CONFIG_CHIP_ID:
+ case CFAM_CONFIG_PEEK_STATUS:
+ if (data == CFAM_CONFIG_CHIP_ID_BREAK) {
+ bus_cold_reset(BUS(&cfam->lbus));
+ }
+ break;
+ default:
+ trace_fsi_cfam_config_write_noaddr(addr, size, data);
+ }
+}
+
+static const struct MemoryRegionOps cfam_config_ops = {
+ .read = fsi_cfam_config_read,
+ .write = fsi_cfam_config_write,
+ .valid.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static uint64_t fsi_cfam_unimplemented_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ trace_fsi_cfam_unimplemented_read(addr, size);
+
+ return 0;
+}
+
+static void fsi_cfam_unimplemented_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ trace_fsi_cfam_unimplemented_write(addr, size, data);
+}
+
+static const struct MemoryRegionOps fsi_cfam_unimplemented_ops = {
+ .read = fsi_cfam_unimplemented_read,
+ .write = fsi_cfam_unimplemented_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void fsi_cfam_instance_init(Object *obj)
+{
+ FSICFAMState *s = FSI_CFAM(obj);
+
+ object_initialize_child(obj, "scratchpad", &s->scratchpad,
+ TYPE_FSI_SCRATCHPAD);
+}
+
+static void fsi_cfam_realize(DeviceState *dev, Error **errp)
+{
+ FSICFAMState *cfam = FSI_CFAM(dev);
+ FSISlaveState *slave = FSI_SLAVE(dev);
+
+ /* Each slave has a 2MiB address space */
+ memory_region_init_io(&cfam->mr, OBJECT(cfam), &fsi_cfam_unimplemented_ops,
+ cfam, TYPE_FSI_CFAM, 2 * MiB);
+
+ qbus_init(&cfam->lbus, sizeof(cfam->lbus), TYPE_FSI_LBUS, DEVICE(cfam),
+ NULL);
+
+ memory_region_init_io(&cfam->config_iomem, OBJECT(cfam), &cfam_config_ops,
+ cfam, TYPE_FSI_CFAM ".config", 0x400);
+
+ memory_region_add_subregion(&cfam->mr, 0, &cfam->config_iomem);
+ memory_region_add_subregion(&cfam->mr, 0x800, &slave->iomem);
+ memory_region_add_subregion(&cfam->mr, 0xc00, &cfam->lbus.mr);
+
+ /* Add scratchpad engine */
+ if (!qdev_realize(DEVICE(&cfam->scratchpad), BUS(&cfam->lbus), errp)) {
+ return;
+ }
+
+ FSILBusDevice *fsi_dev = FSI_LBUS_DEVICE(&cfam->scratchpad);
+ memory_region_add_subregion(&cfam->lbus.mr, 0, &fsi_dev->iomem);
+}
+
+static void fsi_cfam_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->bus_type = TYPE_FSI_BUS;
+ dc->realize = fsi_cfam_realize;
+}
+
+static const TypeInfo fsi_cfam_info = {
+ .name = TYPE_FSI_CFAM,
+ .parent = TYPE_FSI_SLAVE,
+ .instance_init = fsi_cfam_instance_init,
+ .instance_size = sizeof(FSICFAMState),
+ .class_init = fsi_cfam_class_init,
+};
+
+static void fsi_cfam_register_types(void)
+{
+ type_register_static(&fsi_cfam_info);
+}
+
+type_init(fsi_cfam_register_types);
diff --git a/hw/fsi/fsi-master.c b/hw/fsi/fsi-master.c
new file mode 100644
index 0000000..a5f0598
--- /dev/null
+++ b/hw/fsi/fsi-master.c
@@ -0,0 +1,170 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 IBM Corp.
+ *
+ * IBM Flexible Service Interface master
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "trace.h"
+
+#include "hw/fsi/fsi-master.h"
+
+#define TYPE_OP_BUS "opb"
+
+#define TO_REG(x) ((x) >> 2)
+
+#define FSI_MENP0 TO_REG(0x010)
+#define FSI_MENP32 TO_REG(0x014)
+#define FSI_MSENP0 TO_REG(0x018)
+#define FSI_MLEVP0 TO_REG(0x018)
+#define FSI_MSENP32 TO_REG(0x01c)
+#define FSI_MLEVP32 TO_REG(0x01c)
+#define FSI_MCENP0 TO_REG(0x020)
+#define FSI_MREFP0 TO_REG(0x020)
+#define FSI_MCENP32 TO_REG(0x024)
+#define FSI_MREFP32 TO_REG(0x024)
+
+#define FSI_MVER TO_REG(0x074)
+#define FSI_MRESP0 TO_REG(0x0d0)
+
+#define FSI_MRESB0 TO_REG(0x1d0)
+#define FSI_MRESB0_RESET_GENERAL BIT(31)
+#define FSI_MRESB0_RESET_ERROR BIT(30)
+
+static uint64_t fsi_master_read(void *opaque, hwaddr addr, unsigned size)
+{
+ FSIMasterState *s = FSI_MASTER(opaque);
+ int reg = TO_REG(addr);
+
+ trace_fsi_master_read(addr, size);
+
+ if (reg >= FSI_MASTER_NR_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[reg];
+}
+
+static void fsi_master_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ FSIMasterState *s = FSI_MASTER(opaque);
+ int reg = TO_REG(addr);
+
+ trace_fsi_master_write(addr, size, data);
+
+ if (reg >= FSI_MASTER_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out of bounds write: %"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return;
+ }
+
+ switch (reg) {
+ case FSI_MENP0:
+ s->regs[FSI_MENP0] = data;
+ break;
+ case FSI_MENP32:
+ s->regs[FSI_MENP32] = data;
+ break;
+ case FSI_MSENP0:
+ s->regs[FSI_MENP0] |= data;
+ break;
+ case FSI_MSENP32:
+ s->regs[FSI_MENP32] |= data;
+ break;
+ case FSI_MCENP0:
+ s->regs[FSI_MENP0] &= ~data;
+ break;
+ case FSI_MCENP32:
+ s->regs[FSI_MENP32] &= ~data;
+ break;
+ case FSI_MRESP0:
+ /* Perform necessary resets leave register 0 to indicate no errors */
+ break;
+ case FSI_MRESB0:
+ if (data & FSI_MRESB0_RESET_GENERAL) {
+ device_cold_reset(DEVICE(opaque));
+ }
+ if (data & FSI_MRESB0_RESET_ERROR) {
+ /* FIXME: this seems dubious */
+ device_cold_reset(DEVICE(opaque));
+ }
+ break;
+ default:
+ s->regs[reg] = data;
+ }
+}
+
+static const struct MemoryRegionOps fsi_master_ops = {
+ .read = fsi_master_read,
+ .write = fsi_master_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void fsi_master_init(Object *o)
+{
+ FSIMasterState *s = FSI_MASTER(o);
+
+ object_initialize_child(o, "cfam", &s->cfam, TYPE_FSI_CFAM);
+
+ qbus_init(&s->bus, sizeof(s->bus), TYPE_FSI_BUS, DEVICE(s), NULL);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &fsi_master_ops, s,
+ TYPE_FSI_MASTER, 0x10000000);
+ memory_region_init(&s->opb2fsi, OBJECT(s), "fsi.opb2fsi", 0x10000000);
+}
+
+static void fsi_master_realize(DeviceState *dev, Error **errp)
+{
+ FSIMasterState *s = FSI_MASTER(dev);
+
+ if (!qdev_realize(DEVICE(&s->cfam), BUS(&s->bus), errp)) {
+ return;
+ }
+
+ /* address ? */
+ memory_region_add_subregion(&s->opb2fsi, 0, &s->cfam.mr);
+}
+
+static void fsi_master_reset(DeviceState *dev)
+{
+ FSIMasterState *s = FSI_MASTER(dev);
+
+ /* Initialize registers */
+ memset(s->regs, 0, sizeof(s->regs));
+
+ /* ASPEED default */
+ s->regs[FSI_MVER] = 0xe0050101;
+}
+
+static void fsi_master_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->bus_type = TYPE_OP_BUS;
+ dc->desc = "FSI Master";
+ dc->realize = fsi_master_realize;
+ dc->reset = fsi_master_reset;
+}
+
+static const TypeInfo fsi_master_info = {
+ .name = TYPE_FSI_MASTER,
+ .parent = TYPE_DEVICE,
+ .instance_init = fsi_master_init,
+ .instance_size = sizeof(FSIMasterState),
+ .class_init = fsi_master_class_init,
+};
+
+static void fsi_register_types(void)
+{
+ type_register_static(&fsi_master_info);
+}
+
+type_init(fsi_register_types);
diff --git a/hw/fsi/fsi.c b/hw/fsi/fsi.c
new file mode 100644
index 0000000..9a5f4e6
--- /dev/null
+++ b/hw/fsi/fsi.c
@@ -0,0 +1,102 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 IBM Corp.
+ *
+ * IBM Flexible Service Interface
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "trace.h"
+
+#include "hw/fsi/fsi.h"
+
+#define TO_REG(x) ((x) >> 2)
+
+static const TypeInfo fsi_bus_info = {
+ .name = TYPE_FSI_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(FSIBus),
+};
+
+static uint64_t fsi_slave_read(void *opaque, hwaddr addr, unsigned size)
+{
+ FSISlaveState *s = FSI_SLAVE(opaque);
+ int reg = TO_REG(addr);
+
+ trace_fsi_slave_read(addr, size);
+
+ if (reg >= FSI_SLAVE_CONTROL_NR_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[reg];
+}
+
+static void fsi_slave_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ FSISlaveState *s = FSI_SLAVE(opaque);
+ int reg = TO_REG(addr);
+
+ trace_fsi_slave_write(addr, size, data);
+
+ if (reg >= FSI_SLAVE_CONTROL_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out of bounds write: 0x%"HWADDR_PRIx" for %u\n",
+ __func__, addr, size);
+ return;
+ }
+
+ s->regs[reg] = data;
+}
+
+static const struct MemoryRegionOps fsi_slave_ops = {
+ .read = fsi_slave_read,
+ .write = fsi_slave_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void fsi_slave_reset(DeviceState *dev)
+{
+ FSISlaveState *s = FSI_SLAVE(dev);
+
+ /* Initialize registers */
+ memset(s->regs, 0, sizeof(s->regs));
+}
+
+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_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->bus_type = TYPE_FSI_BUS;
+ dc->desc = "FSI Slave";
+ 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_register_types(void)
+{
+ type_register_static(&fsi_bus_info);
+ type_register_static(&fsi_slave_info);
+}
+
+type_init(fsi_register_types);
diff --git a/hw/fsi/lbus.c b/hw/fsi/lbus.c
new file mode 100644
index 0000000..20495f4
--- /dev/null
+++ b/hw/fsi/lbus.c
@@ -0,0 +1,117 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2024 IBM Corp.
+ *
+ * IBM Local bus where FSI slaves are connected
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/fsi/lbus.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "trace.h"
+
+#define TO_REG(offset) ((offset) >> 2)
+
+static void fsi_lbus_init(Object *o)
+{
+ FSILBus *lbus = FSI_LBUS(o);
+
+ memory_region_init(&lbus->mr, OBJECT(lbus), TYPE_FSI_LBUS, 1 * MiB);
+}
+
+static const TypeInfo fsi_lbus_info = {
+ .name = TYPE_FSI_LBUS,
+ .parent = TYPE_BUS,
+ .instance_init = fsi_lbus_init,
+ .instance_size = sizeof(FSILBus),
+};
+
+static const TypeInfo fsi_lbus_device_type_info = {
+ .name = TYPE_FSI_LBUS_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FSILBusDevice),
+ .abstract = true,
+};
+
+static uint64_t fsi_scratchpad_read(void *opaque, hwaddr addr, unsigned size)
+{
+ FSIScratchPad *s = SCRATCHPAD(opaque);
+ int reg = TO_REG(addr);
+
+ trace_fsi_scratchpad_read(addr, size);
+
+ if (reg >= FSI_SCRATCHPAD_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return 0;
+ }
+
+ return s->regs[reg];
+}
+
+static void fsi_scratchpad_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ FSIScratchPad *s = SCRATCHPAD(opaque);
+
+ trace_fsi_scratchpad_write(addr, size, data);
+ int reg = TO_REG(addr);
+
+ if (reg >= FSI_SCRATCHPAD_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return;
+ }
+
+ s->regs[reg] = data;
+}
+
+static const struct MemoryRegionOps scratchpad_ops = {
+ .read = fsi_scratchpad_read,
+ .write = fsi_scratchpad_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void fsi_scratchpad_realize(DeviceState *dev, Error **errp)
+{
+ FSILBusDevice *ldev = FSI_LBUS_DEVICE(dev);
+
+ memory_region_init_io(&ldev->iomem, OBJECT(ldev), &scratchpad_ops,
+ ldev, TYPE_FSI_SCRATCHPAD, 0x400);
+}
+
+static void fsi_scratchpad_reset(DeviceState *dev)
+{
+ FSIScratchPad *s = SCRATCHPAD(dev);
+
+ memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void fsi_scratchpad_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->bus_type = TYPE_FSI_LBUS;
+ dc->realize = fsi_scratchpad_realize;
+ dc->reset = fsi_scratchpad_reset;
+}
+
+static const TypeInfo fsi_scratchpad_info = {
+ .name = TYPE_FSI_SCRATCHPAD,
+ .parent = TYPE_FSI_LBUS_DEVICE,
+ .instance_size = sizeof(FSIScratchPad),
+ .class_init = fsi_scratchpad_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_init(fsi_lbus_register_types);
diff --git a/hw/fsi/meson.build b/hw/fsi/meson.build
new file mode 100644
index 0000000..a18a076
--- /dev/null
+++ b/hw/fsi/meson.build
@@ -0,0 +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_APB2OPB_ASPEED', if_true: files('aspeed_apb2opb.c'))
diff --git a/hw/fsi/trace-events b/hw/fsi/trace-events
new file mode 100644
index 0000000..9e286d0
--- /dev/null
+++ b/hw/fsi/trace-events
@@ -0,0 +1,13 @@
+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_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"
+fsi_cfam_config_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
+fsi_cfam_unimplemented_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d"
+fsi_cfam_unimplemented_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
+fsi_cfam_config_write_noaddr(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
+fsi_master_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d"
+fsi_master_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
+fsi_aspeed_apb2opb_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d"
+fsi_aspeed_apb2opb_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64
diff --git a/hw/fsi/trace.h b/hw/fsi/trace.h
new file mode 100644
index 0000000..ee67c7f
--- /dev/null
+++ b/hw/fsi/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_fsi.h"
diff --git a/hw/meson.build b/hw/meson.build
index f01fac4..463d702 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -44,6 +44,7 @@ subdir('virtio')
subdir('watchdog')
subdir('xen')
subdir('xenpv')
+subdir('fsi')
subdir('alpha')
subdir('arm')