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-----

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>
master
Peter Maydell 2024-02-01 14:42:11 +00:00
commit c3709fde59
30 changed files with 1578 additions and 58 deletions

View File

@ -3585,6 +3585,15 @@ F: tests/qtest/adm1272-test.c
F: tests/qtest/max34451-test.c
F: tests/qtest/isl_pmbus_vr-test.c
FSI
M: Ninad Palsule <ninad@linux.ibm.com>
R: Cédric Le Goater <clg@kaod.org>
S: Maintained
F: hw/fsi/*
F: include/hw/fsi/*
F: docs/specs/fsi.rst
F: tests/qtest/aspeed_fsi-test.c
Firmware schema specifications
M: Philippe Mathieu-Daudé <philmd@linaro.org>
R: Daniel P. Berrange <berrange@redhat.com>

122
docs/specs/fsi.rst Normal file
View File

@ -0,0 +1,122 @@
======================================
IBM's Flexible Service Interface (FSI)
======================================
The QEMU FSI emulation implements hardware interfaces between ASPEED SOC, FSI
master/slave and the end engine.
FSI is a point-to-point two wire interface which is capable of supporting
distances of up to 4 meters. FSI interfaces have been used successfully for
many years in IBM servers to attach IBM Flexible Support Processors(FSP) to
CPUs and IBM ASICs.
FSI allows a service processor access to the internal buses of a host POWER
processor to perform configuration or debugging. FSI has long existed in POWER
processes and so comes with some baggage, including how it has been integrated
into the ASPEED SoC.
Working backwards from the POWER processor, the fundamental pieces of interest
for the implementation are: (see the `FSI specification`_ for more details)
1. The Common FRU Access Macro (CFAM), an address space containing various
"engines" that drive accesses on buses internal and external to the POWER
chip. Examples include the SBEFIFO and I2C masters. The engines hang off of
an internal Local Bus (LBUS) which is described by the CFAM configuration
block.
2. The FSI slave: The slave is the terminal point of the FSI bus for FSI
symbols addressed to it. Slaves can be cascaded off of one another. The
slave's configuration registers appear in address space of the CFAM to
which it is attached.
3. The FSI master: A controller in the platform service processor (e.g. BMC)
driving CFAM engine accesses into the POWER chip. At the hardware level
FSI is a bit-based protocol supporting synchronous and DMA-driven accesses
of engines in a CFAM.
4. The On-Chip Peripheral Bus (OPB): A low-speed bus typically found in POWER
processors. This now makes an appearance in the ASPEED SoC due to tight
integration of the FSI master IP with the OPB, mainly the existence of an
MMIO-mapping of the CFAM address straight onto a sub-region of the OPB
address space.
5. An APB-to-OPB bridge enabling access to the OPB from the ARM core in the
AST2600. Hardware limitations prevent the OPB from being directly mapped
into APB, so all accesses are indirect through the bridge.
The LBUS is modelled to maintain the qdev bus hierarchy and to take advantages
of the object model to automatically generate the CFAM configuration block.
The configuration block presents engines in the order they are attached to the
CFAM's LBUS. Engine implementations should subclass the LBusDevice and set the
'config' member of LBusDeviceClass to match the engine's type.
CFAM designs offer a lot of flexibility, for instance it is possible for a
CFAM to be simultaneously driven from multiple FSI links. The modeling is not
so complete; it's assumed that each CFAM is attached to a single FSI slave (as
a consequence the CFAM subclasses the FSI slave).
As for FSI, its symbols and wire-protocol are not modelled at all. This is not
necessary to get FSI off the ground thanks to the mapping of the CFAM address
space onto the OPB address space - the models follow this directly and map the
CFAM memory region into the OPB's memory region.
The following commands start the ``rainier-bmc`` machine with built-in FSI
model. There are no model specific arguments. Please check this document to
learn more about Aspeed ``rainier-bmc`` machine: (:doc:`../../system/arm/aspeed`)
.. code-block:: console
qemu-system-arm -M rainier-bmc -nographic \
-kernel fitImage-linux.bin \
-dtb aspeed-bmc-ibm-rainier.dtb \
-initrd obmc-phosphor-initramfs.rootfs.cpio.xz \
-drive file=obmc-phosphor-image.rootfs.wic.qcow2,if=sd,index=2 \
-append "rootwait console=ttyS4,115200n8 root=PARTLABEL=rofs-a"
The implementation appears as following in the qemu device tree:
.. code-block:: console
(qemu) info qtree
bus: main-system-bus
type System
...
dev: aspeed.apb2opb, id ""
gpio-out "sysbus-irq" 1
mmio 000000001e79b000/0000000000001000
bus: opb.1
type opb
dev: fsi.master, id ""
bus: fsi.bus.1
type fsi.bus
dev: cfam.config, id ""
dev: cfam, id ""
bus: lbus.1
type lbus
dev: scratchpad, id ""
address = 0 (0x0)
bus: opb.0
type opb
dev: fsi.master, id ""
bus: fsi.bus.0
type fsi.bus
dev: cfam.config, id ""
dev: cfam, id ""
bus: lbus.0
type lbus
dev: scratchpad, id ""
address = 0 (0x0)
pdbg is a simple application to allow debugging of the host POWER processors
from the BMC. (see the `pdbg source repository`_ for more details)
.. code-block:: console
root@p10bmc:~# pdbg -a getcfam 0x0
p0: 0x0 = 0xc0022d15
.. _FSI specification:
https://openpowerfoundation.org/specifications/fsi/
.. _pdbg source repository:
https://github.com/open-power/pdbg

View File

@ -24,6 +24,7 @@ guest hardware that is specific to QEMU.
acpi_erst
sev-guest-firmware
fw_cfg
fsi
vmw_pvscsi-spec
edu
ivshmem-spec

View File

@ -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

View File

@ -580,6 +580,7 @@ config ASPEED_SOC
select LED
select PMBUS
select MAX31785
select FSI_APB2OPB_ASPEED
config MPS2
bool

View File

@ -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[] = {

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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);

7
hw/fsi/Kconfig Normal file
View File

@ -0,0 +1,7 @@
config FSI_APB2OPB_ASPEED
bool
depends on ASPEED_SOC
select FSI
config FSI
bool

367
hw/fsi/aspeed_apb2opb.c Normal file
View File

@ -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);

168
hw/fsi/cfam.c Normal file
View File

@ -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);

170
hw/fsi/fsi-master.c Normal file
View File

@ -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);

102
hw/fsi/fsi.c Normal file
View File

@ -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);

117
hw/fsi/lbus.c Normal file
View File

@ -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);

2
hw/fsi/meson.build Normal file
View File

@ -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'))

13
hw/fsi/trace-events Normal file
View File

@ -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

1
hw/fsi/trace.h Normal file
View File

@ -0,0 +1 @@
#include "trace/trace-hw_fsi.h"

View File

@ -44,6 +44,7 @@ subdir('virtio')
subdir('watchdog')
subdir('xen')
subdir('xenpv')
subdir('fsi')
subdir('alpha')
subdir('arm')

View File

@ -36,6 +36,7 @@
#include "hw/misc/aspeed_lpc.h"
#include "hw/misc/unimp.h"
#include "hw/misc/aspeed_peci.h"
#include "hw/fsi/aspeed_apb2opb.h"
#include "hw/char/serial.h"
#define ASPEED_SPIS_NUM 2
@ -90,6 +91,7 @@ struct AspeedSoCState {
UnimplementedDeviceState udc;
UnimplementedDeviceState sgpiom;
UnimplementedDeviceState jtag[ASPEED_JTAG_NUM];
AspeedAPB2OPBState fsi[2];
};
#define TYPE_ASPEED_SOC "aspeed-soc"
@ -128,7 +130,8 @@ struct AspeedSoCClass {
DeviceClass parent_class;
const char *name;
const char *cpu_type;
/** valid_cpu_types: NULL terminated array of a single CPU type. */
const char * const *valid_cpu_types;
uint32_t silicon_rev;
uint64_t sram_size;
uint64_t secsram_size;
@ -143,6 +146,7 @@ struct AspeedSoCClass {
qemu_irq (*get_irq)(AspeedSoCState *s, int dev);
};
const char *aspeed_soc_cpu_type(AspeedSoCClass *sc);
enum {
ASPEED_DEV_SPI_BOOT,
@ -214,6 +218,8 @@ enum {
ASPEED_DEV_SGPIOM,
ASPEED_DEV_JTAG0,
ASPEED_DEV_JTAG1,
ASPEED_DEV_FSI1,
ASPEED_DEV_FSI2,
};
#define ASPEED_SOC_SPI_BOOT_ADDR 0x0

View File

@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2024 IBM Corp.
*
* ASPEED APB2OPB Bridge
* IBM On-Chip Peripheral Bus
*/
#ifndef FSI_ASPEED_APB2OPB_H
#define FSI_ASPEED_APB2OPB_H
#include "exec/memory.h"
#include "hw/fsi/fsi-master.h"
#include "hw/sysbus.h"
#define TYPE_FSI_OPB "fsi.opb"
#define TYPE_OP_BUS "opb"
OBJECT_DECLARE_SIMPLE_TYPE(OPBus, OP_BUS)
typedef struct OPBus {
BusState bus;
MemoryRegion mr;
AddressSpace as;
} OPBus;
#define TYPE_ASPEED_APB2OPB "aspeed.apb2opb"
OBJECT_DECLARE_SIMPLE_TYPE(AspeedAPB2OPBState, ASPEED_APB2OPB)
#define ASPEED_APB2OPB_NR_REGS ((0xe8 >> 2) + 1)
#define ASPEED_FSI_NUM 2
typedef struct AspeedAPB2OPBState {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t regs[ASPEED_APB2OPB_NR_REGS];
qemu_irq irq;
OPBus opb[ASPEED_FSI_NUM];
FSIMasterState fsi[ASPEED_FSI_NUM];
} AspeedAPB2OPBState;
#endif /* FSI_ASPEED_APB2OPB_H */

34
include/hw/fsi/cfam.h Normal file
View File

@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2024 IBM Corp.
*
* IBM Common FRU Access Macro
*/
#ifndef FSI_CFAM_H
#define FSI_CFAM_H
#include "exec/memory.h"
#include "hw/fsi/fsi.h"
#include "hw/fsi/lbus.h"
#define TYPE_FSI_CFAM "cfam"
#define FSI_CFAM(obj) OBJECT_CHECK(FSICFAMState, (obj), TYPE_FSI_CFAM)
/* P9-ism */
#define CFAM_CONFIG_NR_REGS 0x28
typedef struct FSICFAMState {
/* < private > */
FSISlaveState parent;
/* CFAM config address space */
MemoryRegion config_iomem;
MemoryRegion mr;
FSILBus lbus;
FSIScratchPad scratchpad;
} FSICFAMState;
#endif /* FSI_CFAM_H */

View File

@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2024 IBM Corp.
*
* IBM Flexible Service Interface Master
*/
#ifndef FSI_FSI_MASTER_H
#define FSI_FSI_MASTER_H
#include "exec/memory.h"
#include "hw/qdev-core.h"
#include "hw/fsi/fsi.h"
#include "hw/fsi/cfam.h"
#define TYPE_FSI_MASTER "fsi.master"
OBJECT_DECLARE_SIMPLE_TYPE(FSIMasterState, FSI_MASTER)
#define FSI_MASTER_NR_REGS ((0x2e0 >> 2) + 1)
typedef struct FSIMasterState {
DeviceState parent;
MemoryRegion iomem;
MemoryRegion opb2fsi;
FSIBus bus;
uint32_t regs[FSI_MASTER_NR_REGS];
FSICFAMState cfam;
} FSIMasterState;
#endif /* FSI_FSI_H */

37
include/hw/fsi/fsi.h Normal file
View File

@ -0,0 +1,37 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2024 IBM Corp.
*
* IBM Flexible Service Interface
*/
#ifndef FSI_FSI_H
#define FSI_FSI_H
#include "exec/memory.h"
#include "hw/qdev-core.h"
#include "hw/fsi/lbus.h"
#include "qemu/bitops.h"
/* Bitwise operations at the word level. */
#define BE_GENMASK(hb, lb) MAKE_64BIT_MASK((lb), ((hb) - (lb) + 1))
#define TYPE_FSI_BUS "fsi.bus"
OBJECT_DECLARE_SIMPLE_TYPE(FSIBus, FSI_BUS)
typedef struct FSIBus {
BusState bus;
} FSIBus;
#define TYPE_FSI_SLAVE "fsi.slave"
OBJECT_DECLARE_SIMPLE_TYPE(FSISlaveState, 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 */

43
include/hw/fsi/lbus.h Normal file
View File

@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2024 IBM Corp.
*
* IBM Local bus and connected device structures.
*/
#ifndef FSI_LBUS_H
#define FSI_LBUS_H
#include "hw/qdev-core.h"
#include "qemu/units.h"
#include "exec/memory.h"
#define TYPE_FSI_LBUS_DEVICE "fsi.lbus.device"
OBJECT_DECLARE_SIMPLE_TYPE(FSILBusDevice, FSI_LBUS_DEVICE)
typedef struct FSILBusDevice {
DeviceState parent;
MemoryRegion iomem;
} FSILBusDevice;
#define TYPE_FSI_LBUS "fsi.lbus"
OBJECT_DECLARE_SIMPLE_TYPE(FSILBus, FSI_LBUS)
typedef struct FSILBus {
BusState bus;
MemoryRegion mr;
} FSILBus;
#define TYPE_FSI_SCRATCHPAD "fsi.scratchpad"
#define SCRATCHPAD(obj) OBJECT_CHECK(FSIScratchPad, (obj), TYPE_FSI_SCRATCHPAD)
#define FSI_SCRATCHPAD_NR_REGS 4
typedef struct FSIScratchPad {
FSILBusDevice parent;
uint32_t regs[FSI_SCRATCHPAD_NR_REGS];
} FSIScratchPad;
#endif /* FSI_LBUS_H */

View File

@ -3289,6 +3289,7 @@ if have_system
'hw/char',
'hw/display',
'hw/dma',
'hw/fsi',
'hw/hyperv',
'hw/i2c',
'hw/i386',

View File

@ -155,6 +155,7 @@ class AST2x00Machine(QemuSystemTest):
time.sleep(0.1)
exec_command(self, 'root')
time.sleep(0.1)
exec_command(self, "passw0rd")
def do_test_arm_aspeed_buildroot_poweroff(self):
exec_command_and_wait_for_pattern(self, 'poweroff',
@ -167,14 +168,14 @@ class AST2x00Machine(QemuSystemTest):
"""
image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
'images/ast2500-evb/buildroot-2022.11-2-g15d3648df9/flash.img')
image_hash = ('f96d11db521fe7a2787745e9e391225deeeec3318ee0fc07c8b799b8833dd474')
'images/ast2500-evb/buildroot-2023.11/flash.img')
image_hash = ('c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f')
image_path = self.fetch_asset(image_url, asset_hash=image_hash,
algorithm='sha256')
self.vm.add_args('-device',
'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
self.do_test_arm_aspeed_buildroot_start(image_path, '0x0')
self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'Aspeed AST2500 EVB')
exec_command_and_wait_for_pattern(self,
'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
@ -195,8 +196,8 @@ class AST2x00Machine(QemuSystemTest):
"""
image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
'images/ast2600-evb/buildroot-2022.11-2-g15d3648df9/flash.img')
image_hash = ('e598d86e5ea79671ca8b59212a326c911bc8bea728dec1a1f5390d717a28bb8b')
'images/ast2600-evb/buildroot-2023.11/flash.img')
image_hash = ('b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68')
image_path = self.fetch_asset(image_url, asset_hash=image_hash,
algorithm='sha256')
@ -206,17 +207,17 @@ class AST2x00Machine(QemuSystemTest):
'ds1338,bus=aspeed.i2c.bus.3,address=0x32');
self.vm.add_args('-device',
'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42');
self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00')
self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
exec_command_and_wait_for_pattern(self,
'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
exec_command_and_wait_for_pattern(self,
'cat /sys/class/hwmon/hwmon0/temp1_input', '0')
'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
property='temperature', value=18000);
exec_command_and_wait_for_pattern(self,
'cat /sys/class/hwmon/hwmon0/temp1_input', '18000')
'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
exec_command_and_wait_for_pattern(self,
'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device',
@ -261,7 +262,6 @@ class AST2x00Machine(QemuSystemTest):
self.vm.add_args('-device',
'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
exec_command(self, "passw0rd")
exec_command_and_wait_for_pattern(self,
'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',

View File

@ -0,0 +1,205 @@
/*
* QTest testcases for IBM's Flexible Service Interface (FSI)
*
* Copyright (c) 2023 IBM Corporation
*
* Authors:
* Ninad Palsule <ninad@linux.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "qemu/module.h"
#include "libqtest-single.h"
/* Registers from ast2600 specifications */
#define ASPEED_FSI_ENGINER_TRIGGER 0x04
#define ASPEED_FSI_OPB0_BUS_SELECT 0x10
#define ASPEED_FSI_OPB1_BUS_SELECT 0x28
#define ASPEED_FSI_OPB0_RW_DIRECTION 0x14
#define ASPEED_FSI_OPB1_RW_DIRECTION 0x2c
#define ASPEED_FSI_OPB0_XFER_SIZE 0x18
#define ASPEED_FSI_OPB1_XFER_SIZE 0x30
#define ASPEED_FSI_OPB0_BUS_ADDR 0x1c
#define ASPEED_FSI_OPB1_BUS_ADDR 0x34
#define ASPEED_FSI_INTRRUPT_CLEAR 0x40
#define ASPEED_FSI_INTRRUPT_STATUS 0x48
#define ASPEED_FSI_OPB0_BUS_STATUS 0x80
#define ASPEED_FSI_OPB1_BUS_STATUS 0x8c
#define ASPEED_FSI_OPB0_READ_DATA 0x84
#define ASPEED_FSI_OPB1_READ_DATA 0x90
/*
* FSI Base addresses from the ast2600 specifications.
*/
#define AST2600_OPB_FSI0_BASE_ADDR 0x1e79b000
#define AST2600_OPB_FSI1_BASE_ADDR 0x1e79b100
static uint32_t aspeed_fsi_base_addr;
static uint32_t aspeed_fsi_readl(QTestState *s, uint32_t reg)
{
return qtest_readl(s, aspeed_fsi_base_addr + reg);
}
static void aspeed_fsi_writel(QTestState *s, uint32_t reg, uint32_t val)
{
qtest_writel(s, aspeed_fsi_base_addr + reg, val);
}
/* Setup base address and select register */
static void test_fsi_setup(QTestState *s, uint32_t base_addr)
{
uint32_t curval;
aspeed_fsi_base_addr = base_addr;
/* Set the base select register */
if (base_addr == AST2600_OPB_FSI0_BASE_ADDR) {
/* Unselect FSI1 */
aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x0);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT);
g_assert_cmpuint(curval, ==, 0x0);
/* Select FSI0 */
aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x1);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT);
g_assert_cmpuint(curval, ==, 0x1);
} else if (base_addr == AST2600_OPB_FSI1_BASE_ADDR) {
/* Unselect FSI0 */
aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x0);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT);
g_assert_cmpuint(curval, ==, 0x0);
/* Select FSI1 */
aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x1);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT);
g_assert_cmpuint(curval, ==, 0x1);
} else {
g_assert_not_reached();
}
}
static void test_fsi_reg_change(QTestState *s, uint32_t reg, uint32_t newval)
{
uint32_t base;
uint32_t curval;
base = aspeed_fsi_readl(s, reg);
aspeed_fsi_writel(s, reg, newval);
curval = aspeed_fsi_readl(s, reg);
g_assert_cmpuint(curval, ==, newval);
aspeed_fsi_writel(s, reg, base);
curval = aspeed_fsi_readl(s, reg);
g_assert_cmpuint(curval, ==, base);
}
static void test_fsi0_master_regs(const void *data)
{
QTestState *s = (QTestState *)data;
test_fsi_setup(s, AST2600_OPB_FSI0_BASE_ADDR);
test_fsi_reg_change(s, ASPEED_FSI_OPB0_RW_DIRECTION, 0xF3F4F514);
test_fsi_reg_change(s, ASPEED_FSI_OPB0_XFER_SIZE, 0xF3F4F518);
test_fsi_reg_change(s, ASPEED_FSI_OPB0_BUS_ADDR, 0xF3F4F51c);
test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_CLEAR, 0xF3F4F540);
test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_STATUS, 0xF3F4F548);
test_fsi_reg_change(s, ASPEED_FSI_OPB0_BUS_STATUS, 0xF3F4F580);
test_fsi_reg_change(s, ASPEED_FSI_OPB0_READ_DATA, 0xF3F4F584);
}
static void test_fsi1_master_regs(const void *data)
{
QTestState *s = (QTestState *)data;
test_fsi_setup(s, AST2600_OPB_FSI1_BASE_ADDR);
test_fsi_reg_change(s, ASPEED_FSI_OPB1_RW_DIRECTION, 0xF3F4F514);
test_fsi_reg_change(s, ASPEED_FSI_OPB1_XFER_SIZE, 0xF3F4F518);
test_fsi_reg_change(s, ASPEED_FSI_OPB1_BUS_ADDR, 0xF3F4F51c);
test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_CLEAR, 0xF3F4F540);
test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_STATUS, 0xF3F4F548);
test_fsi_reg_change(s, ASPEED_FSI_OPB1_BUS_STATUS, 0xF3F4F580);
test_fsi_reg_change(s, ASPEED_FSI_OPB1_READ_DATA, 0xF3F4F584);
}
static void test_fsi0_getcfam_addr0(const void *data)
{
QTestState *s = (QTestState *)data;
uint32_t curval;
test_fsi_setup(s, AST2600_OPB_FSI0_BASE_ADDR);
/* Master access direction read */
aspeed_fsi_writel(s, ASPEED_FSI_OPB0_RW_DIRECTION, 0x1);
/* word */
aspeed_fsi_writel(s, ASPEED_FSI_OPB0_XFER_SIZE, 0x3);
/* Address */
aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_ADDR, 0xa0000000);
aspeed_fsi_writel(s, ASPEED_FSI_INTRRUPT_CLEAR, 0x1);
aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1);
curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS);
g_assert_cmpuint(curval, ==, 0x10000);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_STATUS);
g_assert_cmpuint(curval, ==, 0x0);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_READ_DATA);
g_assert_cmpuint(curval, ==, 0x152d02c0);
}
static void test_fsi1_getcfam_addr0(const void *data)
{
QTestState *s = (QTestState *)data;
uint32_t curval;
test_fsi_setup(s, AST2600_OPB_FSI1_BASE_ADDR);
/* Master access direction read */
aspeed_fsi_writel(s, ASPEED_FSI_OPB1_RW_DIRECTION, 0x1);
aspeed_fsi_writel(s, ASPEED_FSI_OPB1_XFER_SIZE, 0x3);
aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_ADDR, 0xa0000000);
aspeed_fsi_writel(s, ASPEED_FSI_INTRRUPT_CLEAR, 0x1);
aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1);
curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS);
g_assert_cmpuint(curval, ==, 0x20000);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_STATUS);
g_assert_cmpuint(curval, ==, 0x0);
curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_READ_DATA);
g_assert_cmpuint(curval, ==, 0x152d02c0);
}
int main(int argc, char **argv)
{
int ret = -1;
QTestState *s;
g_test_init(&argc, &argv, NULL);
s = qtest_init("-machine ast2600-evb ");
/* Tests for OPB/FSI0 */
qtest_add_data_func("/aspeed-fsi-test/test_fsi0_master_regs", s,
test_fsi0_master_regs);
qtest_add_data_func("/aspeed-fsi-test/test_fsi0_getcfam_addr0", s,
test_fsi0_getcfam_addr0);
/* Tests for OPB/FSI1 */
qtest_add_data_func("/aspeed-fsi-test/test_fsi1_master_regs", s,
test_fsi1_master_regs);
qtest_add_data_func("/aspeed-fsi-test/test_fsi1_getcfam_addr0", s,
test_fsi1_getcfam_addr0);
ret = g_test_run();
qtest_quit(s);
return ret;
}

View File

@ -217,6 +217,7 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \
(config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
(config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \
['arm-cpu-features',
'boot-serial-test']