diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 60ed94f187..5d0a7865d3 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -31,6 +31,7 @@ AST2600 SoC based machines : - ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC - ``rainier-bmc`` IBM Rainier POWER10 BMC - ``fuji-bmc`` Facebook Fuji BMC +- ``fby35-bmc`` Facebook fby35 BMC Supported devices ----------------- @@ -120,3 +121,64 @@ FMC chip and a bigger (64M) SPI chip, use : .. code-block:: bash -M ast2500-evb,fmc-model=mx25l25635e,spi-model=mx66u51235f + + +Aspeed minibmc family boards (``ast1030-evb``) +================================================================== + +The QEMU Aspeed machines model mini BMCs of various Aspeed evaluation +boards. They are based on different releases of the +Aspeed SoC : the AST1030 integrating an ARM Cortex M4F CPU (200MHz). + +The SoC comes with SRAM, SPI, I2C, etc. + +AST1030 SoC based machines : + +- ``ast1030-evb`` Aspeed AST1030 Evaluation board (Cortex-M4F) + +Supported devices +----------------- + + * SMP (for the AST1030 Cortex-M4F) + * Interrupt Controller (VIC) + * Timer Controller + * I2C Controller + * System Control Unit (SCU) + * SRAM mapping + * Static Memory Controller (SMC or FMC) - Only SPI Flash support + * SPI Memory Controller + * USB 2.0 Controller + * Watchdog Controller + * GPIO Controller (Master only) + * UART + * LPC Peripheral Controller (a subset of subdevices are supported) + * Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA + * ADC + + +Missing devices +--------------- + + * PWM and Fan Controller + * Slave GPIO Controller + * PECI Controller + * Mailbox Controller + * Virtual UART + * eSPI Controller + * I3C Controller + +Boot options +------------ + +The Aspeed machines can be started using the ``-kernel`` to load a +Zephyr OS or from a firmware. Images can be downloaded from the +ASPEED GitHub release repository : + + https://github.com/AspeedTech-BMC/zephyr/releases + +To boot a kernel directly from a Zephyr build tree: + +.. code-block:: bash + + $ qemu-system-arm -M ast1030-evb -nographic \ + -kernel zephyr.elf diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a74c13ab0f..98dc185acd 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -21,6 +21,7 @@ #include "hw/misc/led.h" #include "hw/qdev-properties.h" #include "sysemu/block-backend.h" +#include "sysemu/reset.h" #include "hw/loader.h" #include "qemu/error-report.h" #include "qemu/units.h" @@ -526,8 +527,15 @@ static void ast2500_evb_i2c_init(AspeedMachineState *bmc) static void ast2600_evb_i2c_init(AspeedMachineState *bmc) { - /* Start with some devices on our I2C busses */ - ast2500_evb_i2c_init(bmc); + AspeedSoCState *soc = &bmc->soc; + uint8_t *eeprom_buf = g_malloc0(8 * 1024); + + smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, + eeprom_buf); + + /* LM75 is compatible with TMP105 driver */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), + TYPE_TMP105, 0x4d); } static void romulus_bmc_i2c_init(AspeedMachineState *bmc) @@ -951,6 +959,35 @@ static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[12], TYPE_PCA9552, 0x67); } +static void fby35_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + I2CBus *i2c[16]; + + for (int i = 0; i < 16; i++) { + i2c[i] = aspeed_i2c_get_bus(&soc->i2c, i); + } + + i2c_slave_create_simple(i2c[2], TYPE_LM75, 0x4f); + i2c_slave_create_simple(i2c[8], TYPE_TMP421, 0x1f); + /* Hotswap controller is actually supposed to be mp5920 or ltc4282. */ + i2c_slave_create_simple(i2c[11], "adm1272", 0x44); + i2c_slave_create_simple(i2c[12], TYPE_LM75, 0x4e); + i2c_slave_create_simple(i2c[12], TYPE_LM75, 0x4f); + + aspeed_eeprom_init(i2c[4], 0x51, 128 * KiB); + aspeed_eeprom_init(i2c[6], 0x51, 128 * KiB); + aspeed_eeprom_init(i2c[8], 0x50, 32 * KiB); + aspeed_eeprom_init(i2c[11], 0x51, 128 * KiB); + aspeed_eeprom_init(i2c[11], 0x54, 128 * KiB); + + /* + * TODO: There is a multi-master i2c connection to an AST1030 MiniBMC on + * buses 0, 1, 2, 3, and 9. Source address 0x10, target address 0x20 on + * each. + */ +} + static bool aspeed_get_mmio_exec(Object *obj, Error **errp) { return ASPEED_MACHINE(obj)->mmio_exec; @@ -1293,6 +1330,35 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) aspeed_soc_num_cpus(amc->soc_name); } +static void fby35_reset(MachineState *state) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(state); + AspeedGPIOState *gpio = &bmc->soc.gpio; + + qemu_devices_reset(); + + /* Board ID */ + object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioV5", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioV6", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioV7", false, &error_fatal); +} + +static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Facebook fby35 BMC (Cortex-A7)"; + mc->reset = fby35_reset; + amc->fmc_model = "mx66l1g45g"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC3_ON; + amc->i2c_init = fby35_i2c_init; + /* FIXME: Replace this macro with something more general */ + mc->default_ram_size = FUJI_BMC_RAM_SIZE; +} + #define AST1030_INTERNAL_FLASH_SIZE (1024 * 1024) /* Main SYSCLK frequency in Hz (200MHz) */ #define SYSCLK_FRQ 200000000ULL @@ -1411,6 +1477,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("bletchley-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_bletchley_class_init, + }, { + .name = MACHINE_TYPE_NAME("fby35-bmc"), + .parent = MACHINE_TYPE_NAME("ast2600-evb"), + .class_init = aspeed_machine_fby35_class_init, }, { .name = MACHINE_TYPE_NAME("ast1030-evb"), .parent = TYPE_ASPEED_MACHINE, diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 4271549282..d534541684 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -15,7 +15,6 @@ #include "sysemu/sysemu.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" -#include "hw/char/serial.h" #include "hw/arm/aspeed_soc.h" #define ASPEED_SOC_IOMEM_SIZE 0x00200000 @@ -33,14 +32,38 @@ static const hwaddr aspeed_soc_ast1030_memmap[] = { [ASPEED_DEV_SBC] = 0x7E6F2000, [ASPEED_DEV_GPIO] = 0x7E780000, [ASPEED_DEV_TIMER1] = 0x7E782000, + [ASPEED_DEV_UART1] = 0x7E783000, + [ASPEED_DEV_UART2] = 0x7E78D000, + [ASPEED_DEV_UART3] = 0x7E78E000, + [ASPEED_DEV_UART4] = 0x7E78F000, [ASPEED_DEV_UART5] = 0x7E784000, + [ASPEED_DEV_UART6] = 0x7E790000, + [ASPEED_DEV_UART7] = 0x7E790100, + [ASPEED_DEV_UART8] = 0x7E790200, + [ASPEED_DEV_UART9] = 0x7E790300, + [ASPEED_DEV_UART10] = 0x7E790400, + [ASPEED_DEV_UART11] = 0x7E790500, + [ASPEED_DEV_UART12] = 0x7E790600, + [ASPEED_DEV_UART13] = 0x7E790700, [ASPEED_DEV_WDT] = 0x7E785000, [ASPEED_DEV_LPC] = 0x7E789000, [ASPEED_DEV_I2C] = 0x7E7B0000, }; static const int aspeed_soc_ast1030_irqmap[] = { + [ASPEED_DEV_UART1] = 47, + [ASPEED_DEV_UART2] = 48, + [ASPEED_DEV_UART3] = 49, + [ASPEED_DEV_UART4] = 50, [ASPEED_DEV_UART5] = 8, + [ASPEED_DEV_UART6] = 57, + [ASPEED_DEV_UART7] = 58, + [ASPEED_DEV_UART8] = 59, + [ASPEED_DEV_UART9] = 60, + [ASPEED_DEV_UART10] = 61, + [ASPEED_DEV_UART11] = 62, + [ASPEED_DEV_UART12] = 63, + [ASPEED_DEV_UART13] = 64, [ASPEED_DEV_GPIO] = 11, [ASPEED_DEV_TIMER1] = 16, [ASPEED_DEV_TIMER2] = 17, @@ -61,11 +84,11 @@ static const int aspeed_soc_ast1030_irqmap[] = { [ASPEED_DEV_KCS] = 138, /* 138 -> 142 */ }; -static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) +static qemu_irq aspeed_soc_ast1030_get_irq(AspeedSoCState *s, int dev) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[ctrl]); + return qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[dev]); } static void aspeed_soc_ast1030_init(Object *obj) @@ -113,6 +136,9 @@ static void aspeed_soc_ast1030_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); } + + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); + object_initialize_child(obj, "gpio", &s->gpio, typename); } static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) @@ -191,10 +217,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); - /* UART5 - attach an 8250 to the IO space as our UART */ - serial_mm_init(get_system_memory(), sc->memmap[ASPEED_DEV_UART5], 2, - aspeed_soc_get_irq(s, ASPEED_DEV_UART5), - 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); + /* UART */ + aspeed_soc_uart_init(s); /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -260,6 +284,14 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); } + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); } static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) @@ -277,9 +309,11 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) sc->ehcis_num = 0; sc->wdts_num = 4; sc->macs_num = 1; + sc->uarts_num = 13; sc->irqmap = aspeed_soc_ast1030_irqmap; sc->memmap = aspeed_soc_ast1030_memmap; sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast1030_get_irq; } static const TypeInfo aspeed_soc_ast1030_type_info = { diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index eedda7badc..b0a4199b69 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -11,7 +11,6 @@ #include "qapi/error.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" -#include "hw/char/serial.h" #include "qemu/module.h" #include "qemu/error-report.h" #include "hw/i2c/aspeed_i2c.h" @@ -61,7 +60,18 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, [ASPEED_DEV_UART1] = 0x1E783000, + [ASPEED_DEV_UART2] = 0x1E78D000, + [ASPEED_DEV_UART3] = 0x1E78E000, + [ASPEED_DEV_UART4] = 0x1E78F000, [ASPEED_DEV_UART5] = 0x1E784000, + [ASPEED_DEV_UART6] = 0x1E790000, + [ASPEED_DEV_UART7] = 0x1E790100, + [ASPEED_DEV_UART8] = 0x1E790200, + [ASPEED_DEV_UART9] = 0x1E790300, + [ASPEED_DEV_UART10] = 0x1E790400, + [ASPEED_DEV_UART11] = 0x1E790500, + [ASPEED_DEV_UART12] = 0x1E790600, + [ASPEED_DEV_UART13] = 0x1E790700, [ASPEED_DEV_VUART] = 0x1E787000, [ASPEED_DEV_I3C] = 0x1E7A0000, [ASPEED_DEV_SDRAM] = 0x80000000, @@ -78,6 +88,14 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_UART3] = 49, [ASPEED_DEV_UART4] = 50, [ASPEED_DEV_UART5] = 8, + [ASPEED_DEV_UART6] = 57, + [ASPEED_DEV_UART7] = 58, + [ASPEED_DEV_UART8] = 59, + [ASPEED_DEV_UART9] = 60, + [ASPEED_DEV_UART10] = 61, + [ASPEED_DEV_UART11] = 62, + [ASPEED_DEV_UART12] = 63, + [ASPEED_DEV_UART13] = 64, [ASPEED_DEV_VUART] = 8, [ASPEED_DEV_FMC] = 39, [ASPEED_DEV_SDMC] = 0, @@ -114,11 +132,11 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_I3C] = 102, /* 102 -> 107 */ }; -static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) +static qemu_irq aspeed_soc_ast2600_get_irq(AspeedSoCState *s, int dev) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ctrl]); + return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[dev]); } static void aspeed_soc_ast2600_init(Object *obj) @@ -353,10 +371,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); - /* UART - attach an 8250 to the IO space as our UART */ - serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, - aspeed_soc_get_irq(s, s->uart_default), 38400, - serial_hd(0), DEVICE_LITTLE_ENDIAN); + /* UART */ + aspeed_soc_uart_init(s); /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -569,9 +585,11 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) sc->ehcis_num = 2; sc->wdts_num = 4; sc->macs_num = 4; + sc->uarts_num = 13; sc->irqmap = aspeed_soc_ast2600_irqmap; sc->memmap = aspeed_soc_ast2600_memmap; sc->num_cpus = 2; + sc->get_irq = aspeed_soc_ast2600_get_irq; } static const TypeInfo aspeed_soc_ast2600_type_info = { diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 58714cb2a0..30574d4276 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -48,6 +48,9 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, + [ASPEED_DEV_UART2] = 0x1E78D000, + [ASPEED_DEV_UART3] = 0x1E78E000, + [ASPEED_DEV_UART4] = 0x1E78F000, [ASPEED_DEV_UART5] = 0x1E784000, [ASPEED_DEV_VUART] = 0x1E787000, [ASPEED_DEV_SDRAM] = 0x40000000, @@ -80,6 +83,9 @@ static const hwaddr aspeed_soc_ast2500_memmap[] = { [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, + [ASPEED_DEV_UART2] = 0x1E78D000, + [ASPEED_DEV_UART3] = 0x1E78E000, + [ASPEED_DEV_UART4] = 0x1E78F000, [ASPEED_DEV_UART5] = 0x1E784000, [ASPEED_DEV_VUART] = 0x1E787000, [ASPEED_DEV_SDRAM] = 0x80000000, @@ -121,11 +127,11 @@ static const int aspeed_soc_ast2400_irqmap[] = { #define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap -static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl) +static qemu_irq aspeed_soc_ast2400_get_irq(AspeedSoCState *s, int dev) { AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[ctrl]); + return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[dev]); } static void aspeed_soc_init(Object *obj) @@ -297,10 +303,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); - /* UART - attach an 8250 to the IO space as our UART */ - serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, - aspeed_soc_get_irq(s, s->uart_default), 38400, - serial_hd(0), DEVICE_LITTLE_ENDIAN); + /* UART */ + aspeed_soc_uart_init(s); /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -484,9 +488,11 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) sc->ehcis_num = 1; sc->wdts_num = 2; sc->macs_num = 2; + sc->uarts_num = 5; sc->irqmap = aspeed_soc_ast2400_irqmap; sc->memmap = aspeed_soc_ast2400_memmap; sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast2400_get_irq; } static const TypeInfo aspeed_soc_ast2400_type_info = { @@ -509,9 +515,11 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) sc->ehcis_num = 2; sc->wdts_num = 3; sc->macs_num = 2; + sc->uarts_num = 5; sc->irqmap = aspeed_soc_ast2500_irqmap; sc->memmap = aspeed_soc_ast2500_memmap; sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast2400_get_irq; } static const TypeInfo aspeed_soc_ast2500_type_info = { @@ -528,4 +536,28 @@ static void aspeed_soc_register_types(void) type_register_static(&aspeed_soc_ast2500_type_info); }; -type_init(aspeed_soc_register_types) +type_init(aspeed_soc_register_types); + +qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) +{ + return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); +} + +void aspeed_soc_uart_init(AspeedSoCState *s) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i, uart; + + /* Attach an 8250 to the IO space as our UART */ + serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, + aspeed_soc_get_irq(s, s->uart_default), 38400, + serial_hd(0), DEVICE_LITTLE_ENDIAN); + for (i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { + if (uart == s->uart_default) { + uart++; + } + serial_mm_init(get_system_memory(), sc->memmap[uart], 2, + aspeed_soc_get_irq(s, uart), 38400, + serial_hd(i), DEVICE_LITTLE_ENDIAN); + } +} diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 7d3d8b12e0..81ba3da4df 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1533,6 +1533,7 @@ static int m25p80_pre_save(void *opaque) static Property m25p80_properties[] = { /* This is default value for Micron flash */ + DEFINE_PROP_BOOL("write-enable", Flash, write_enable, false), DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), DEFINE_PROP_UINT8("spansion-cr1nv", Flash, spansion_cr1nv, 0x0), DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8), diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 9b736e7a9f..a62a673857 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -15,6 +15,8 @@ #include "qapi/visitor.h" #include "hw/irq.h" #include "migration/vmstate.h" +#include "trace.h" +#include "hw/registerfields.h" #define GPIOS_PER_GROUP 8 @@ -203,6 +205,28 @@ #define GPIO_1_8V_MEM_SIZE 0x1D8 #define GPIO_1_8V_REG_ARRAY_SIZE (GPIO_1_8V_MEM_SIZE >> 2) +/* + * GPIO index mode support + * It only supports write operation + */ +REG32(GPIO_INDEX_REG, 0x2AC) + FIELD(GPIO_INDEX_REG, NUMBER, 0, 8) + FIELD(GPIO_INDEX_REG, COMMAND, 12, 1) + FIELD(GPIO_INDEX_REG, TYPE, 16, 4) + FIELD(GPIO_INDEX_REG, DATA_VALUE, 20, 1) + FIELD(GPIO_INDEX_REG, DIRECTION, 20, 1) + FIELD(GPIO_INDEX_REG, INT_ENABLE, 20, 1) + FIELD(GPIO_INDEX_REG, INT_SENS_0, 21, 1) + FIELD(GPIO_INDEX_REG, INT_SENS_1, 22, 1) + FIELD(GPIO_INDEX_REG, INT_SENS_2, 23, 1) + FIELD(GPIO_INDEX_REG, INT_STATUS, 24, 1) + FIELD(GPIO_INDEX_REG, DEBOUNCE_1, 20, 1) + FIELD(GPIO_INDEX_REG, DEBOUNCE_2, 21, 1) + FIELD(GPIO_INDEX_REG, RESET_TOLERANT, 20, 1) + FIELD(GPIO_INDEX_REG, COMMAND_SRC_0, 20, 1) + FIELD(GPIO_INDEX_REG, COMMAND_SRC_1, 21, 1) + FIELD(GPIO_INDEX_REG, INPUT_MASK, 20, 1) + static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio) { uint32_t falling_edge = 0, rising_edge = 0; @@ -523,55 +547,214 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) uint64_t idx = -1; const AspeedGPIOReg *reg; GPIOSets *set; + uint32_t value = 0; + uint64_t debounce_value; idx = offset >> 2; if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) { idx -= GPIO_DEBOUNCE_TIME_1; - return (uint64_t) s->debounce_regs[idx]; + debounce_value = (uint64_t) s->debounce_regs[idx]; + trace_aspeed_gpio_read(offset, debounce_value); + return debounce_value; } reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" - HWADDR_PRIx"\n", __func__, offset); + PRIx64"\n", __func__, offset); return 0; } set = &s->sets[reg->set_idx]; switch (reg->type) { case gpio_reg_data_value: - return set->data_value; + value = set->data_value; + break; case gpio_reg_direction: - return set->direction; + value = set->direction; + break; case gpio_reg_int_enable: - return set->int_enable; + value = set->int_enable; + break; case gpio_reg_int_sens_0: - return set->int_sens_0; + value = set->int_sens_0; + break; case gpio_reg_int_sens_1: - return set->int_sens_1; + value = set->int_sens_1; + break; case gpio_reg_int_sens_2: - return set->int_sens_2; + value = set->int_sens_2; + break; case gpio_reg_int_status: - return set->int_status; + value = set->int_status; + break; case gpio_reg_reset_tolerant: - return set->reset_tol; + value = set->reset_tol; + break; case gpio_reg_debounce_1: - return set->debounce_1; + value = set->debounce_1; + break; case gpio_reg_debounce_2: - return set->debounce_2; + value = set->debounce_2; + break; case gpio_reg_cmd_source_0: - return set->cmd_source_0; + value = set->cmd_source_0; + break; case gpio_reg_cmd_source_1: - return set->cmd_source_1; + value = set->cmd_source_1; + break; case gpio_reg_data_read: - return set->data_read; + value = set->data_read; + break; case gpio_reg_input_mask: - return set->input_mask; + value = set->input_mask; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" - HWADDR_PRIx"\n", __func__, offset); + PRIx64"\n", __func__, offset); return 0; } + + trace_aspeed_gpio_read(offset, value); + return value; +} + +static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, + uint64_t data, uint32_t size) +{ + + AspeedGPIOState *s = ASPEED_GPIO(opaque); + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + const GPIOSetProperties *props; + GPIOSets *set; + uint32_t reg_idx_number = FIELD_EX32(data, GPIO_INDEX_REG, NUMBER); + uint32_t reg_idx_type = FIELD_EX32(data, GPIO_INDEX_REG, TYPE); + uint32_t reg_idx_command = FIELD_EX32(data, GPIO_INDEX_REG, COMMAND); + uint32_t set_idx = reg_idx_number / ASPEED_GPIOS_PER_SET; + uint32_t pin_idx = reg_idx_number % ASPEED_GPIOS_PER_SET; + uint32_t group_idx = pin_idx / GPIOS_PER_GROUP; + uint32_t reg_value = 0; + uint32_t cleared; + + set = &s->sets[set_idx]; + props = &agc->props[set_idx]; + + if (reg_idx_command) + qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%" PRIx64 "data 0x%" + PRIx64 "index mode wrong command 0x%x\n", + __func__, offset, data, reg_idx_command); + + switch (reg_idx_type) { + case gpio_reg_idx_data: + reg_value = set->data_read; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, DATA_VALUE)); + reg_value &= props->output; + reg_value = update_value_control_source(set, set->data_value, + reg_value); + set->data_read = reg_value; + aspeed_gpio_update(s, set, reg_value); + return; + case gpio_reg_idx_direction: + reg_value = set->direction; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, DIRECTION)); + /* + * where data is the value attempted to be written to the pin: + * pin type | input mask | output mask | expected value + * ------------------------------------------------------------ + * bidirectional | 1 | 1 | data + * input only | 1 | 0 | 0 + * output only | 0 | 1 | 1 + * no pin | 0 | 0 | 0 + * + * which is captured by: + * data = ( data | ~input) & output; + */ + reg_value = (reg_value | ~props->input) & props->output; + set->direction = update_value_control_source(set, set->direction, + reg_value); + break; + case gpio_reg_idx_interrupt: + reg_value = set->int_enable; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INT_ENABLE)); + set->int_enable = update_value_control_source(set, set->int_enable, + reg_value); + reg_value = set->int_sens_0; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_0)); + set->int_sens_0 = update_value_control_source(set, set->int_sens_0, + reg_value); + reg_value = set->int_sens_1; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_1)); + set->int_sens_1 = update_value_control_source(set, set->int_sens_1, + reg_value); + reg_value = set->int_sens_2; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_2)); + set->int_sens_2 = update_value_control_source(set, set->int_sens_2, + reg_value); + /* set interrupt status */ + reg_value = set->int_status; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)); + cleared = ctpop32(reg_value & set->int_status); + if (s->pending && cleared) { + assert(s->pending >= cleared); + s->pending -= cleared; + } + set->int_status &= ~reg_value; + break; + case gpio_reg_idx_debounce: + reg_value = set->debounce_1; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, DEBOUNCE_1)); + set->debounce_1 = update_value_control_source(set, set->debounce_1, + reg_value); + reg_value = set->debounce_2; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, DEBOUNCE_2)); + set->debounce_2 = update_value_control_source(set, set->debounce_2, + reg_value); + return; + case gpio_reg_idx_tolerance: + reg_value = set->reset_tol; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, RESET_TOLERANT)); + set->reset_tol = update_value_control_source(set, set->reset_tol, + reg_value); + return; + case gpio_reg_idx_cmd_src: + reg_value = set->cmd_source_0; + reg_value = deposit32(reg_value, GPIOS_PER_GROUP * group_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, COMMAND_SRC_0)); + set->cmd_source_0 = reg_value & ASPEED_CMD_SRC_MASK; + reg_value = set->cmd_source_1; + reg_value = deposit32(reg_value, GPIOS_PER_GROUP * group_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, COMMAND_SRC_1)); + set->cmd_source_1 = reg_value & ASPEED_CMD_SRC_MASK; + return; + case gpio_reg_idx_input_mask: + reg_value = set->input_mask; + reg_value = deposit32(reg_value, pin_idx, 1, + FIELD_EX32(data, GPIO_INDEX_REG, INPUT_MASK)); + /* + * feeds into interrupt generation + * 0: read from data value reg will be updated + * 1: read from data value reg will not be updated + */ + set->input_mask = reg_value & props->input; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%" PRIx64 "data 0x%" + PRIx64 "index mode wrong type 0x%x\n", + __func__, offset, data, reg_idx_type); + return; + } + aspeed_gpio_update(s, set, set->data_value); + return; } static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, @@ -585,7 +768,16 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, GPIOSets *set; uint32_t cleared; + trace_aspeed_gpio_write(offset, data); + idx = offset >> 2; + + /* check gpio index mode */ + if (idx == R_GPIO_INDEX_REG) { + aspeed_gpio_write_index_mode(opaque, offset, data, size); + return; + } + if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) { idx -= GPIO_DEBOUNCE_TIME_1; s->debounce_regs[idx] = (uint32_t) data; @@ -595,7 +787,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" - HWADDR_PRIx"\n", __func__, offset); + PRIx64"\n", __func__, offset); return; } @@ -680,7 +872,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" - HWADDR_PRIx"\n", __func__, offset); + PRIx64"\n", __func__, offset); return; } aspeed_gpio_update(s, set, set->data_value); @@ -795,6 +987,15 @@ static GPIOSetProperties ast2600_1_8v_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [1] = {0x0000000f, 0x0000000f, {"18E"} }, }; +static GPIOSetProperties ast1030_set_props[ASPEED_GPIO_MAX_NR_SETS] = { + [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, + [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, + [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, + [3] = {0xffffff3f, 0xffffff3f, {"M", "N", "O", "P"} }, + [4] = {0xff060c1f, 0x00060c1f, {"Q", "R", "S", "T"} }, + [5] = {0x000000ff, 0x00000000, {"U"} }, +}; + static const MemoryRegionOps aspeed_gpio_ops = { .read = aspeed_gpio_read, .write = aspeed_gpio_write, @@ -947,6 +1148,16 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) agc->reg_table = aspeed_1_8v_gpios; } +static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); + + agc->props = ast1030_set_props; + agc->nr_gpio_pins = 151; + agc->nr_gpio_sets = 6; + agc->reg_table = aspeed_3_3v_gpios; +} + static const TypeInfo aspeed_gpio_info = { .name = TYPE_ASPEED_GPIO, .parent = TYPE_SYS_BUS_DEVICE, @@ -984,6 +1195,13 @@ static const TypeInfo aspeed_gpio_ast2600_1_8v_info = { .instance_init = aspeed_gpio_init, }; +static const TypeInfo aspeed_gpio_ast1030_info = { + .name = TYPE_ASPEED_GPIO "-ast1030", + .parent = TYPE_ASPEED_GPIO, + .class_init = aspeed_gpio_1030_class_init, + .instance_init = aspeed_gpio_init, +}; + static void aspeed_gpio_register_types(void) { type_register_static(&aspeed_gpio_info); @@ -991,6 +1209,7 @@ static void aspeed_gpio_register_types(void) type_register_static(&aspeed_gpio_ast2500_info); type_register_static(&aspeed_gpio_ast2600_3_3v_info); type_register_static(&aspeed_gpio_ast2600_1_8v_info); + type_register_static(&aspeed_gpio_ast1030_info); } type_init(aspeed_gpio_register_types); diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 1dab99c560..9736b362ac 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -27,3 +27,7 @@ sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" P sifive_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 sifive_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 + +# aspeed_gpio.c +aspeed_gpio_read(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64 +aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index e13af374b9..02a5a9ffcb 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -91,9 +91,11 @@ struct AspeedSoCClass { int ehcis_num; int wdts_num; int macs_num; + int uarts_num; const int *irqmap; const hwaddr *memmap; uint32_t num_cpus; + qemu_irq (*get_irq)(AspeedSoCState *s, int dev); }; @@ -104,6 +106,14 @@ enum { ASPEED_DEV_UART3, ASPEED_DEV_UART4, ASPEED_DEV_UART5, + ASPEED_DEV_UART6, + ASPEED_DEV_UART7, + ASPEED_DEV_UART8, + ASPEED_DEV_UART9, + ASPEED_DEV_UART10, + ASPEED_DEV_UART11, + ASPEED_DEV_UART12, + ASPEED_DEV_UART13, ASPEED_DEV_VUART, ASPEED_DEV_FMC, ASPEED_DEV_SPI1, @@ -153,4 +163,7 @@ enum { ASPEED_DEV_I3C, }; +qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); +void aspeed_soc_uart_init(AspeedSoCState *s); + #endif /* ASPEED_SOC_H */ diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index 6dee3cd438..904eecf62c 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -50,10 +50,24 @@ enum GPIORegType { gpio_reg_input_mask, }; +/* GPIO index mode */ +enum GPIORegIndexType { + gpio_reg_idx_data = 0, + gpio_reg_idx_direction, + gpio_reg_idx_interrupt, + gpio_reg_idx_debounce, + gpio_reg_idx_tolerance, + gpio_reg_idx_cmd_src, + gpio_reg_idx_input_mask, + gpio_reg_idx_reserved, + gpio_reg_idx_new_w_cmd_src, + gpio_reg_idx_new_r_cmd_src, +}; + typedef struct AspeedGPIOReg { uint16_t set_idx; enum GPIORegType type; - } AspeedGPIOReg; +} AspeedGPIOReg; struct AspeedGPIOClass { SysBusDevice parent_obj; diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index c1003f2d1b..bac63e8742 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -28,30 +28,6 @@ #include "qapi/qmp/qdict.h" #include "libqtest-single.h" -static bool qom_get_bool(QTestState *s, const char *path, const char *property) -{ - QDict *r; - bool b; - - r = qtest_qmp(s, "{ 'execute': 'qom-get', 'arguments': " - "{ 'path': %s, 'property': %s } }", path, property); - b = qdict_get_bool(r, "return"); - qobject_unref(r); - - return b; -} - -static void qom_set_bool(QTestState *s, const char *path, const char *property, - bool value) -{ - QDict *r; - - r = qtest_qmp(s, "{ 'execute': 'qom-set', 'arguments': " - "{ 'path': %s, 'property': %s, 'value': %i } }", - path, property, value); - qobject_unref(r); -} - static void test_set_colocated_pins(const void *data) { QTestState *s = (QTestState *)data; @@ -60,14 +36,14 @@ static void test_set_colocated_pins(const void *data) * gpioV4-7 occupy bits within a single 32-bit value, so we want to make * sure that modifying one doesn't affect the other. */ - qom_set_bool(s, "/machine/soc/gpio", "gpioV4", true); - qom_set_bool(s, "/machine/soc/gpio", "gpioV5", false); - qom_set_bool(s, "/machine/soc/gpio", "gpioV6", true); - qom_set_bool(s, "/machine/soc/gpio", "gpioV7", false); - g_assert(qom_get_bool(s, "/machine/soc/gpio", "gpioV4")); - g_assert(!qom_get_bool(s, "/machine/soc/gpio", "gpioV5")); - g_assert(qom_get_bool(s, "/machine/soc/gpio", "gpioV6")); - g_assert(!qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); + qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV4", true); + qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV5", false); + qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV6", true); + qtest_qom_set_bool(s, "/machine/soc/gpio", "gpioV7", false); + g_assert(qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV4")); + g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV5")); + g_assert(qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV6")); + g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); } int main(int argc, char **argv) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 87b40a0ef1..ec233315e6 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" #include "libqtest-single.h" +#include "qemu/bitops.h" /* * ASPEED SPI Controller registers @@ -40,6 +41,7 @@ #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 #define CTRL_USERMODE 0x3 +#define SR_WEL BIT(1) #define ASPEED_FMC_BASE 0x1E620000 #define ASPEED_FLASH_BASE 0x20000000 @@ -49,6 +51,8 @@ */ enum { JEDEC_READ = 0x9f, + RDSR = 0x5, + WRDI = 0x4, BULK_ERASE = 0xc7, READ = 0x03, PP = 0x02, @@ -348,6 +352,44 @@ static void test_write_page_mem(void) flash_reset(); } +static void test_read_status_reg(void) +{ + uint8_t r; + + spi_conf(CONF_ENABLE_W0); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + + g_assert_cmphex(r & SR_WEL, ==, SR_WEL); + g_assert(qtest_qom_get_bool + (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WRDI); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + + flash_reset(); +} + static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX"; int main(int argc, char **argv) @@ -373,6 +415,7 @@ int main(int argc, char **argv) qtest_add_func("/ast2400/smc/write_page", test_write_page); qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); + qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); ret = g_test_run(); diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 2e49618454..8c159eacf5 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -1440,3 +1440,27 @@ void qtest_client_inproc_recv(void *opaque, const char *str) g_string_append(qts->rx, str); return; } + +void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, + bool value) +{ + QDict *r; + + r = qtest_qmp(s, "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': %s, 'property': %s, 'value': %i } }", + path, property, value); + qobject_unref(r); +} + +bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property) +{ + QDict *r; + bool b; + + r = qtest_qmp(s, "{ 'execute': 'qom-get', 'arguments': " + "{ 'path': %s, 'property': %s } }", path, property); + b = qdict_get_bool(r, "return"); + qobject_unref(r); + + return b; +} diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 4ab0cad326..94b187837d 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -783,4 +783,26 @@ QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch, void (*send)(void*, const char*)); void qtest_client_inproc_recv(void *opaque, const char *str); + +/** + * qtest_qom_set_bool: + * @s: QTestState instance to operate on. + * @path: Path to the property being set. + * @property: Property being set. + * @value: Value to set the property. + * + * Set the property with passed in value. + */ +void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, + bool value); + +/** + * qtest_qom_get_bool: + * @s: QTestState instance to operate on. + * @path: Path to the property being retrieved. + * @property: Property from where the value is being retrieved. + * + * Returns: Value retrieved from property. + */ +bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property); #endif