/* * ASPEED AST1700 IO Expander * * Copyright (C) 2025 ASPEED Technology Inc. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "hw/core/boards.h" #include "qom/object.h" #include "qapi/error.h" #include "hw/core/qdev-properties.h" #include "hw/arm/aspeed_ast1700.h" #define AST2700_SOC_LTPI_SIZE 0x01000000 #define AST1700_SOC_SRAM_SIZE 0x00040000 #define AST1700_SOC_I3C_SIZE 0x00010000 enum { ASPEED_AST1700_DEV_SPI0, ASPEED_AST1700_DEV_PWM, ASPEED_AST1700_DEV_SRAM, ASPEED_AST1700_DEV_ADC, ASPEED_AST1700_DEV_SCU, ASPEED_AST1700_DEV_GPIO, ASPEED_AST1700_DEV_SGPIOM0, ASPEED_AST1700_DEV_SGPIOM1, ASPEED_AST1700_DEV_I2C, ASPEED_AST1700_DEV_I3C, ASPEED_AST1700_DEV_UART12, ASPEED_AST1700_DEV_LTPI_CTRL, ASPEED_AST1700_DEV_WDT, ASPEED_AST1700_DEV_SPI0_MEM, }; static const hwaddr aspeed_ast1700_io_memmap[] = { [ASPEED_AST1700_DEV_SPI0] = 0x00030000, [ASPEED_AST1700_DEV_PWM] = 0x000C0000, [ASPEED_AST1700_DEV_SRAM] = 0x00BC0000, [ASPEED_AST1700_DEV_ADC] = 0x00C00000, [ASPEED_AST1700_DEV_SCU] = 0x00C02000, [ASPEED_AST1700_DEV_GPIO] = 0x00C0B000, [ASPEED_AST1700_DEV_SGPIOM0] = 0x00C0C000, [ASPEED_AST1700_DEV_SGPIOM1] = 0x00C0D000, [ASPEED_AST1700_DEV_I2C] = 0x00C0F000, [ASPEED_AST1700_DEV_I3C] = 0x00C20000, [ASPEED_AST1700_DEV_UART12] = 0x00C33B00, [ASPEED_AST1700_DEV_LTPI_CTRL] = 0x00C34000, [ASPEED_AST1700_DEV_WDT] = 0x00C37000, [ASPEED_AST1700_DEV_SPI0_MEM] = 0x04000000, }; static void aspeed_ast1700_realize(DeviceState *dev, Error **errp) { AspeedAST1700SoCState *s = ASPEED_AST1700(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); char dev_name[32]; if (!s->dram_mr) { error_setg(errp, TYPE_ASPEED_AST1700 ": 'dram' link not set"); return; } /* Occupy memory space for all controllers in AST1700 */ memory_region_init(&s->iomem, OBJECT(s), TYPE_ASPEED_AST1700, AST2700_SOC_LTPI_SIZE); sysbus_init_mmio(sbd, &s->iomem); /* SRAM */ snprintf(dev_name, sizeof(dev_name), "aspeed.ioexp-sram.%d", s->board_idx); memory_region_init_ram(&s->sram, OBJECT(s), dev_name, AST1700_SOC_SRAM_SIZE, errp); memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_SRAM], &s->sram); /* UART */ qdev_prop_set_uint8(DEVICE(&s->uart), "regshift", 2); qdev_prop_set_uint32(DEVICE(&s->uart), "baudbase", 38400); qdev_prop_set_uint8(DEVICE(&s->uart), "endianness", DEVICE_LITTLE_ENDIAN); if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_UART12], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0)); /* SPI */ object_property_set_link(OBJECT(&s->spi), "dram", OBJECT(s->dram_mr), errp); if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_SPI0], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->spi), 0)); memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_SPI0_MEM], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->spi), 1)); /* ADC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_ADC], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->adc), 0)); /* SCU */ qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", s->silicon_rev); if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_SCU], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->scu), 0)); /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_GPIO], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); /* I2C */ snprintf(dev_name, sizeof(dev_name), "ioexp%d", s->board_idx); qdev_prop_set_string(DEVICE(&s->i2c), "bus-label", dev_name); object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), errp); if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_I2C], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c), 0)); /* PWM */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pwm), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_PWM], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pwm), 0)); /* LTPI controller */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->ltpi), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_LTPI_CTRL], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ltpi), 0)); /* SGPIOM */ for (int i = 0; i < AST1700_SGPIO_NUM; i++) { if (!sysbus_realize(SYS_BUS_DEVICE(&s->sgpiom[i]), errp)) { return; } memory_region_add_subregion(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_SGPIOM0 + i], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sgpiom[i]), 0)); } /* WDT */ for (int i = 0; i < AST1700_WDT_NUM; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); hwaddr wdt_offset = aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_WDT] + i * awc->iosize; object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), errp); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } memory_region_add_subregion(&s->iomem, wdt_offset, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->wdt[i]), 0)); } /* I3C */ qdev_prop_set_string(DEVICE(&s->i3c), "name", "ioexp-i3c"); qdev_prop_set_uint64(DEVICE(&s->i3c), "size", AST1700_SOC_I3C_SIZE); sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp); memory_region_add_subregion_overlap(&s->iomem, aspeed_ast1700_io_memmap[ASPEED_AST1700_DEV_I3C], sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i3c), 0), -1000); } static void aspeed_ast1700_instance_init(Object *obj) { AspeedAST1700SoCState *s = ASPEED_AST1700(obj); /* UART */ object_initialize_child(obj, "uart", &s->uart, TYPE_SERIAL_MM); /* SPI */ object_initialize_child(obj, "ioexp-spi", &s->spi, "aspeed.spi0-ast2700"); /* ADC */ object_initialize_child(obj, "ioexp-adc", &s->adc, "aspeed.adc-ast2700"); /* SCU */ object_initialize_child(obj, "ioexp-scu", &s->scu, TYPE_ASPEED_2700_SCU); /* GPIO */ object_initialize_child(obj, "ioexp-gpio", &s->gpio, "aspeed.gpio-ast2700"); /* I2C */ object_initialize_child(obj, "ioexp-i2c", &s->i2c, "aspeed.i2c-ast2700"); /* PWM */ object_initialize_child(obj, "pwm", &s->pwm, TYPE_ASPEED_PWM); /* LTPI controller */ object_initialize_child(obj, "ltpi-ctrl", &s->ltpi, TYPE_ASPEED_LTPI); /* SGPIOM */ for (int i = 0; i < AST1700_SGPIO_NUM; i++) { object_initialize_child(obj, "ioexp-sgpiom[*]", &s->sgpiom[i], "aspeed.sgpio-ast2700"); } /* WDT */ for (int i = 0; i < AST1700_WDT_NUM; i++) { object_initialize_child(obj, "ioexp-wdt[*]", &s->wdt[i], "aspeed.wdt-ast2700"); } /* I3C */ object_initialize_child(obj, "ioexp-i3c", &s->i3c, TYPE_UNIMPLEMENTED_DEVICE); return; } static const Property aspeed_ast1700_props[] = { DEFINE_PROP_UINT8("board-idx", AspeedAST1700SoCState, board_idx, 0), DEFINE_PROP_UINT32("silicon-rev", AspeedAST1700SoCState, silicon_rev, 0), DEFINE_PROP_LINK("dram", AspeedAST1700SoCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), }; static void aspeed_ast1700_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_ast1700_realize; device_class_set_props(dc, aspeed_ast1700_props); } static const TypeInfo aspeed_ast1700_types[] = { { .name = TYPE_ASPEED_AST1700, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedAST1700SoCState), .class_init = aspeed_ast1700_class_init, .instance_init = aspeed_ast1700_instance_init, } }; DEFINE_TYPES(aspeed_ast1700_types)