As the only I2C emulation in QEMU that supports being both a master and a slave, suitable for MCTP over i2c is aspeed-i2c add this controller to the arm virt model and hook up our new i2c_mctp_cxl_fmapi device.
The current Linux driver for aspeed-i2c has a hard requirement on a reset controller. Throw down the simplest reset controller I could find so as to avoid need to make any changes to the kernel code. Patch also builds appropriate device tree. Signed-off-by: Jonathan Cameron <jonathan.came...@huawei.com> --- include/hw/arm/virt.h | 2 + hw/arm/virt.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 9fc582fc5f..ea3a64f4a8 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -72,6 +72,8 @@ enum { VIRT_SMMU, VIRT_UART, VIRT_MMIO, + VIRT_I2C, + VIRT_RESET_FAKE, VIRT_RTC, VIRT_FW_CFG, VIRT_PCIE, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 5512ad4ba4..67e694550e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -85,6 +85,8 @@ #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_host.h" #include "qemu/guest-random.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/aspeed_i2c.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -161,6 +163,8 @@ static const MemMapEntry base_memmap[] = { [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + [VIRT_I2C] = { 0x0b000000, 0x00004000 }, + [VIRT_RESET_FAKE] = { 0x0b004000, 0x00000010 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 }, @@ -203,6 +207,7 @@ static const int a15irqmap[] = { [VIRT_GPIO] = 7, [VIRT_SECURE_UART] = 8, [VIRT_ACPI_GED] = 9, + [VIRT_I2C] = 10, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ @@ -2268,6 +2273,85 @@ static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem) } } +static void create_mctp(MachineState *ms) +{ + VirtMachineState *vms = VIRT_MACHINE(ms); + MemoryRegion *sysmem = get_system_memory(); + AspeedI2CState *aspeedi2c; + struct DeviceState *dev; + char *nodename_i2c_master; + char *nodename_i2c_sub; + char *nodename_reset; + uint32_t clk_phandle, reset_phandle; + MemoryRegion *sysmem2; + + dev = qdev_new("aspeed.i2c-ast2600"); + aspeedi2c = ASPEED_I2C(dev); + object_property_set_link(OBJECT(dev), "dram", OBJECT(ms->ram), + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_I2C].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&aspeedi2c->busses[0]), 0, + qdev_get_gpio_in(vms->gic, vms->irqmap[VIRT_I2C])); + + /* I2C bus DT */ + reset_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename_reset = g_strdup_printf("/reset@%" PRIx64, + vms->memmap[VIRT_RESET_FAKE].base); + qemu_fdt_add_subnode(ms->fdt, nodename_reset); + qemu_fdt_setprop_string(ms->fdt, nodename_reset, + "compatible", "snps,dw-low-reset"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename_reset, "reg", + 2, vms->memmap[VIRT_RESET_FAKE].base, + 2, vms->memmap[VIRT_RESET_FAKE].size); + qemu_fdt_setprop_cell(ms->fdt, nodename_reset, "#reset-cells", 0x1); + qemu_fdt_setprop_cell(ms->fdt, nodename_reset, "phandle", reset_phandle); + sysmem2 = g_new(MemoryRegion, 1); + memory_region_init_ram(sysmem2, NULL, "reset", + vms->memmap[VIRT_RESET_FAKE].size, NULL); + memory_region_add_subregion(sysmem, + vms->memmap[VIRT_RESET_FAKE].base, sysmem2); + + clk_phandle = qemu_fdt_alloc_phandle(ms->fdt); + + qemu_fdt_add_subnode(ms->fdt, "/mclk"); + qemu_fdt_setprop_string(ms->fdt, "/mclk", "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(ms->fdt, "/mclk", "#clock-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, "/mclk", "clock-frequency", 24000); + qemu_fdt_setprop_string(ms->fdt, "/mclk", "clock-output-names", "bobsclk"); + qemu_fdt_setprop_cell(ms->fdt, "/mclk", "phandle", clk_phandle); + + nodename_i2c_master = g_strdup_printf("/i2c@%" PRIx64, + vms->memmap[VIRT_I2C].base); + qemu_fdt_add_subnode(ms->fdt, nodename_i2c_master); + qemu_fdt_setprop_string(ms->fdt, nodename_i2c_master, + "compatible", "aspeed,ast2600-i2c-bus"); + qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master, "multi-master"); + qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "#size-cells", 0); + qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "#address-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "clocks", clk_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename_i2c_master, + "clock-names", "bobsclk"); + qemu_fdt_setprop(ms->fdt, nodename_i2c_master, "mctp-controller", NULL, 0); + qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master, + "interrupts", GIC_FDT_IRQ_TYPE_SPI, + vms->irqmap[VIRT_I2C], GIC_FDT_IRQ_FLAGS_LEVEL_HI); + /* Offset to the first bus is 0x80, next one at 0x100 etc */ + qemu_fdt_setprop_sized_cells(ms->fdt, nodename_i2c_master, "reg", + 2, vms->memmap[VIRT_I2C].base + 0x80, + 2, 0x80); + qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master, + "resets", reset_phandle, 0); + + nodename_i2c_sub = g_strdup_printf("/i2c@%" PRIx64 "/mctp@%" PRIx64, + vms->memmap[VIRT_I2C].base, 0x50l); + qemu_fdt_add_subnode(ms->fdt, nodename_i2c_sub); + qemu_fdt_setprop_string(ms->fdt, nodename_i2c_sub, + "compatible", "mctp-i2c-controller"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename_i2c_sub, + "reg", 1, 0x50 | 0x40000000); +} + static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); @@ -2555,6 +2639,8 @@ static void machvirt_init(MachineState *machine) create_gpio_devices(vms, VIRT_SECURE_GPIO, secure_sysmem); } + create_mctp(machine); + /* connect powerdown request */ vms->powerdown_notifier.notify = virt_powerdown_req; qemu_register_powerdown_notifier(&vms->powerdown_notifier); -- 2.39.2