[PATCH v2 7/8] ufs/aspeed: Add AST2700 sysbus UFS frontend
Mikail Sadic <[email protected]> Thu, 30 Jul 2026 15:09:42 -0500
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
Add TYPE_ASPEED_UFS, a sysbus frontend around the common UFS core, and wire it into the AST2700 SoC. The frontend reuses UfsHc (instance_size = sizeof(UfsHc)), sets dma_as to address_space_memory, and calls ufs_realize_core() before exposing its MMIO region and IRQ via sysbus. It reports UFSHCI 2.0 (reg.ver = 0x0200) to match the AST2700 (aspeed,ufshc-m31-16nm). Signed-off-by: Mikail Sadic <[email protected]> --- MAINTAINERS | 1 + docs/specs/aspeed-ufs.rst | 40 ++++++++++++++++++++ docs/specs/index.rst | 1 + include/hw/arm/aspeed.h | 1 + include/hw/arm/aspeed_soc.h | 3 ++ include/hw/ufs/aspeed_ufs.h | 16 ++++++++ hw/arm/aspeed.c | 25 +++++++++++++ hw/arm/aspeed_ast27x0.c | 13 +++++++ hw/ufs/aspeed_ufs.c | 75 +++++++++++++++++++++++++++++++++++++ hw/ufs/meson.build | 1 + 10 files changed, 176 insertions(+) create mode 100644 docs/specs/aspeed-ufs.rst create mode 100644 include/hw/ufs/aspeed_ufs.h create mode 100644 hw/ufs/aspeed_ufs.c diff --git a/MAINTAINERS b/MAINTAINERS index ec415cb8e4..abebae2ff9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2700,6 +2700,7 @@ M: Jeuk Kim <[email protected]> S: Supported F: hw/ufs/* F: include/block/ufs.h +F: include/hw/ufs/aspeed_ufs.h F: tests/qtest/ufs-test.c megasas diff --git a/docs/specs/aspeed-ufs.rst b/docs/specs/aspeed-ufs.rst new file mode 100644 index 0000000000..596b5d2756 --- /dev/null +++ b/docs/specs/aspeed-ufs.rst @@ -0,0 +1,40 @@ +ASPEED AST2700 UFS Host Controller +=================================== + +The AST2700 SoC includes a UFS host controller identified in the device tree +as ``aspeed,ufshc-m31-16nm``, mapped at ``0x12c08200`` (IRQ SPI 118). QEMU +models it as a sysbus frontend on top of the shared UFS core that also backs +the PCI UFS device (``hw/ufs/ufs.c`` and ``hw/ufs/lu.c``). The frontend only +provides the sysbus MMIO region, the interrupt line and the DMA address +space; all UFSHCI register behaviour, UTP transfer/task list processing, +UPIU and query handling and the SCSI logical-unit logic are implemented by +the core. + +The clock/reset wrapper at ``0x12c08000`` (``aspeed,ast2700-ufscnr``) is left +as an ``UnimplementedDevice``. + +Logical units +------------- + +Storage is attached through ``ufs-lu`` devices on the controller's UFS bus +(``ufs-bus.0``), exactly as for the PCI UFS device. The Huygens OpenBMC image +is laid out for 512-byte sectors, so its logical unit is created with +``logical-block-size=512``. + +Usage +----- + +Attach a UFS image as logical unit 0 of the controller's UFS bus: + +.. code-block:: console + + qemu-system-aarch64 -M huygens-bmc \ + -nodefaults \ + -blockdev node-name=fmc0,driver=file,filename=image-bmc \ + -device w25q01jvq,bus=ssi.0,cs=0,drive=fmc0 \ + -blockdev node-name=ufs0,driver=file,filename=ufs.img \ + -device ufs-lu,bus=ufs-bus.0,drive=ufs0,lun=0,logical-block-size=512 \ + -display none -serial mon:stdio + +Please check :doc:`../../system/arm/aspeed` for more details on the +``huygens-bmc`` machine. diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 4de65e2fdf..dd0cdec8d4 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -40,4 +40,5 @@ guest hardware that is specific to QEMU. riscv-aia aspeed-intc ucd90320 + aspeed-ufs iommu-testdev diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index a00238ed74..dedc784926 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -62,6 +62,7 @@ struct AspeedMachineClass { uint32_t uart_default; bool sdhci_wp_inverted; bool vbootrom; + uint32_t ufs_block_size; }; /* diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 41dc04e293..56fab0a350 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -35,6 +35,7 @@ #include "hw/gpio/aspeed_gpio.h" #include "hw/gpio/aspeed_sgpio.h" #include "hw/sd/aspeed_sdhci.h" +#include "hw/ufs/aspeed_ufs.h" #include "hw/usb/hcd-ehci.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" @@ -118,6 +119,7 @@ struct AspeedSoCState { AspeedAPB2OPBState fsi[2]; AspeedLTPIState ltpi_ctrl[ASPEED_IOEXP_NUM]; AspeedAST1700SoCState ioexp[ASPEED_IOEXP_NUM]; + UfsHc ufs; }; #define TYPE_ASPEED_SOC "aspeed-soc" @@ -298,6 +300,7 @@ enum { ASPEED_DEV_PRIC0, ASPEED_DEV_PRIC1, ASPEED_DEV_OTP, + ASPEED_DEV_UFS, }; const char *aspeed_soc_cpu_type(const char * const *valid_cpu_types); diff --git a/include/hw/ufs/aspeed_ufs.h b/include/hw/ufs/aspeed_ufs.h new file mode 100644 index 0000000000..1ccd0b51c0 --- /dev/null +++ b/include/hw/ufs/aspeed_ufs.h @@ -0,0 +1,16 @@ +/* + * ASPEED AST2700 UFS Host Controller + * + * Copyright 2026 IBM Corp. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_UFS_H +#define ASPEED_UFS_H + +#include "hw/ufs/ufs.h" + +#define TYPE_ASPEED_UFS "aspeed-ufs" +#define ASPEED_UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_ASPEED_UFS) + +#endif /* ASPEED_UFS_H */ diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a48c442058..5b9943a0e0 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -187,6 +187,31 @@ static void aspeed_machine_init(MachineState *machine) aspeed_connect_serial_hds_to_uarts(bmc); qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); + /* + * If the SoC instantiated a UFS host controller (AST2700), attach the + * first backend drive to it as logical unit 0. The controller and its + * UFS bus only exist once the SoC has been realized, so the ufs-lu is + * created here rather than through a controller drive property. + */ + if (object_resolve_path_component(OBJECT(bmc->soc), "ufs")) { + DriveInfo *ufs_dinfo = drive_get(IF_NONE, 0, 0); + + if (ufs_dinfo) { + DeviceState *ufs_lu = qdev_new(TYPE_UFS_LU); + + qdev_prop_set_uint8(ufs_lu, "lun", 0); + if (amc->ufs_block_size) { + qdev_prop_set_uint32(ufs_lu, "logical-block-size", + amc->ufs_block_size); + } + qdev_prop_set_drive_err(ufs_lu, "drive", + blk_by_legacy_dinfo(ufs_dinfo), + &error_fatal); + qdev_realize_and_unref(ufs_lu, BUS(&bmc->soc->ufs.bus), + &error_fatal); + } + } + if (defaults_enabled()) { aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index b908d7d4ff..1ee8f3a210 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -46,6 +46,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_GIC_REDIST] = 0x12280000, [ASPEED_DEV_SDMC] = 0x12C00000, [ASPEED_DEV_SCU] = 0x12C02000, + [ASPEED_DEV_UFS] = 0x12c08200, [ASPEED_DEV_RTC] = 0x12C0F000, [ASPEED_DEV_TIMER1] = 0x12C10000, [ASPEED_DEV_PCIE_PHY0] = 0x12C15000, @@ -129,6 +130,7 @@ static const int aspeed_soc_ast2700a1_irqmap[] = { [ASPEED_DEV_EHCI2] = 37, [ASPEED_DEV_PCIE0] = 56, [ASPEED_DEV_PCIE1] = 57, + [ASPEED_DEV_UFS] = 118, [ASPEED_DEV_LPC] = 192, [ASPEED_DEV_IBT] = 192, [ASPEED_DEV_KCS] = 192, @@ -533,6 +535,8 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "emmc-controller.sdhci", &s->emmc.slots[0], TYPE_SYSBUS_SDHCI); + object_initialize_child(obj, "ufs", &s->ufs, TYPE_ASPEED_UFS); + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); @@ -1039,6 +1043,15 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_EMMC)); + /* UFS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ufs), errp)) { + return; + } + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ufs), 0, + sc->memmap[ASPEED_DEV_UFS]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ufs), 0, + aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_UFS)); + /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), &error_abort); diff --git a/hw/ufs/aspeed_ufs.c b/hw/ufs/aspeed_ufs.c new file mode 100644 index 0000000000..d3e1a9a736 --- /dev/null +++ b/hw/ufs/aspeed_ufs.c @@ -0,0 +1,75 @@ +/* + * ASPEED AST2700 UFS Host Controller + * + * Sysbus frontend for the AST2700 UFS host controller + * (aspeed,ufshc-m31-16nm). The UFSHCI register interface, UTRL/UTMRL + * processing, UPIU/query handling and the SCSI/logical-unit logic are all + * provided by the shared UFS core (hw/ufs/ufs.c, hw/ufs/lu.c); this file + * only supplies the sysbus-specific MMIO, IRQ and DMA plumbing. + * + * The ASPEED clock/reset wrapper at 0x12c08000 (aspeed,ast2700-ufscnr) is + * modelled elsewhere as an UnimplementedDevice. + * + * Copyright 2026 IBM Corp. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/core/qdev-properties.h" +#include "system/address-spaces.h" +#include "hw/ufs/aspeed_ufs.h" + +static void aspeed_ufs_realize(DeviceState *dev, Error **errp) +{ + UfsHc *u = ASPEED_UFS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + /* AST2700 UFS masters DMA into system memory. */ + u->dma_as = &address_space_memory; + + if (!ufs_realize_core(u, errp)) { + return; + } + + /* + * The AST2700 host controller (aspeed,ufshc-m31-16nm) reports UFSHCI + * version 2.0. The shared core defaults to 4.1, which makes the U-Boot + * and Linux aspeed-ufs drivers run 4.x-only probe steps that this model + * does not implement. Pin the controller version register to 2.0. + */ + u->reg.ver = 0x00000200; + + ufs_init_mmio(u); + sysbus_init_mmio(sbd, &u->iomem); + sysbus_init_irq(sbd, &u->irq); +} + +static const Property aspeed_ufs_props[] = { + DEFINE_PROP_STRING("serial", UfsHc, params.serial), + DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), + DEFINE_PROP_UINT32("wb-max-size", UfsHc, params.wb_max_size, 0x400), + DEFINE_PROP_UINT32("wb-min-size", UfsHc, params.wb_min_size, 0x100), +}; + +static void aspeed_ufs_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_ufs_realize; + dc->desc = "ASPEED UFS Host Controller"; + device_class_set_props(dc, aspeed_ufs_props); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo aspeed_ufs_types[] = { + { + .name = TYPE_ASPEED_UFS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UfsHc), + .class_init = aspeed_ufs_class_init, + }, +}; + +DEFINE_TYPES(aspeed_ufs_types) diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build index 6e68328b93..86052e6fd2 100644 --- a/hw/ufs/meson.build +++ b/hw/ufs/meson.build @@ -1 +1,2 @@ system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_ufs.c')) -- 2.53.0