All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Cédric Le Goater" <clg@kaod.org>
To: Jamin Lin <jamin_lin@aspeedtech.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Peter Maydell <peter.maydell@linaro.org>,
	Steven Lee <steven_lee@aspeedtech.com>,
	Troy Lee <leetroy@gmail.com>,
	Kane Chen <kane_chen@aspeedtech.com>,
	Andrew Jeffery <andrew@codeconstruct.com.au>,
	Joel Stanley <joel@jms.id.au>,
	"open list:ARM TCG CPUs" <qemu-arm@nongnu.org>,
	"open list:All patches CC here" <qemu-devel@nongnu.org>
Cc: "Troy Lee" <troy_lee@aspeedtech.com>,
	"Philippe Mathieu-Daudé" <philmd@oss.qualcomm.com>
Subject: Re: [PATCH v4 1/4] hw/usb/aspeed-udc: Add ASPEED UDC device controller
Date: Tue, 1 Sep 2026 08:18:07 +0200	[thread overview]
Message-ID: <d44087a5-bfd5-4e73-bda8-2fd3d940d16b@kaod.org> (raw)
In-Reply-To: <20260901024400.3488429-2-jamin_lin@aspeedtech.com>

On 9/1/26 04:44, Jamin Lin wrote:
> The AST2600 has a USB 2.0 Device Controller (UDC) at 0x1e6a2000 with one
> control endpoint and four programmable endpoints.
> 
> Add the controller as a sysbus (system) device: the MMIO register map
> described with the registerfields macros, the interrupt line and the
> soft reset. This is only the register/system side.
> 
> Note: this "device controller" is the system-bus device (TYPE_ASPEED_UDC).
> It is not the gadget USB device (TYPE_ASPEED_UDC_GADGET) that a host
> controller enumerates, which is added in the next patch.
> 
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> ---
>   include/hw/usb/aspeed-udc.h |  49 ++++++++
>   hw/usb/aspeed-udc.c         | 236 ++++++++++++++++++++++++++++++++++++
>   hw/arm/Kconfig              |   1 +
>   hw/usb/Kconfig              |   3 +
>   hw/usb/meson.build          |   1 +
>   hw/usb/trace-events         |   7 ++
>   6 files changed, 297 insertions(+)
>   create mode 100644 include/hw/usb/aspeed-udc.h
>   create mode 100644 hw/usb/aspeed-udc.c
> 
> diff --git a/include/hw/usb/aspeed-udc.h b/include/hw/usb/aspeed-udc.h
> new file mode 100644
> index 0000000000..58fed5f9a2
> --- /dev/null
> +++ b/include/hw/usb/aspeed-udc.h
> @@ -0,0 +1,49 @@
> +/*
> + * ASPEED USB Device Controller (UDC)
> + *
> + * Copyright (c) 2026 ASPEED Technology Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_USB_ASPEED_UDC_H
> +#define HW_USB_ASPEED_UDC_H
> +
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_ASPEED_UDC "aspeed.udc"
> +OBJECT_DECLARE_SIMPLE_TYPE(AspeedUDCState, ASPEED_UDC)
> +
> +/*
> + * Register map: root/global block at 0x000 - 0x087, then one 0x10 byte bank
> + * per programmable endpoint from 0x200.
> + */
> +#define ASPEED_UDC_MEM_SIZE     0x300
> +#define ASPEED_UDC_ROOT_NR_REGS (0x88 >> 2)
> +#define ASPEED_UDC_EP_REG_BASE  0x200
> +#define ASPEED_UDC_EP_NR_REGS   (0x10 >> 2)
> +
> +/*
> + * EP0 (control) is served through the root registers (UDC_EP0_*), so only
> + * the 4 programmable endpoints get their own register bank / ep[] entry.
> + */
> +#define ASPEED_UDC_NUM_EP   4
> +
> +typedef struct AspeedUDCEP {
> +    MemoryRegion mr;
> +    uint32_t regs[ASPEED_UDC_EP_NR_REGS];
> +    int index;
> +} AspeedUDCEP;
> +
> +struct AspeedUDCState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion udc_container;
> +    MemoryRegion root_mr;
> +    uint32_t regs[ASPEED_UDC_ROOT_NR_REGS];
> +    AspeedUDCEP ep[ASPEED_UDC_NUM_EP];
> +    qemu_irq irq;
> +};
> +
> +#endif /* HW_USB_ASPEED_UDC_H */
> diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c
> new file mode 100644
> index 0000000000..cd773e979b
> --- /dev/null
> +++ b/hw/usb/aspeed-udc.c
> @@ -0,0 +1,236 @@
> +/*
> + * ASPEED USB Device Controller (UDC)
> + *
> + * Copyright (c) 2026 ASPEED Technology Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Models the ASPEED USB Device Controller (UDC). It implements one control
> + * endpoint (EP0) and 4 programmable endpoints.
> + *
> + * This file is the system-bus side of the controller: the MMIO register map,
> + * the interrupt and the soft reset. The gadget USB device presented to a host
> + * controller (and the endpoint data path) is added on top of this.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/core/irq.h"
> +#include "hw/core/registerfields.h"
> +#include "hw/usb/aspeed-udc.h"
> +#include "qemu/module.h"
> +#include "trace.h"
> +
> +/* Root / Global registers (offset from the controller base) */
> +REG32(UDC_FUNC_CTRL, 0x00)
> +    FIELD(UDC_FUNC_CTRL, UPSTREAM_EN,       0, 1)
> +REG32(UDC_IER, 0x08)
> +REG32(UDC_ISR, 0x0C)
> +    FIELD(UDC_ISR, EP_POOL_ACK,            16, 1)
> +    FIELD(UDC_ISR, BUS_RESET,               6, 1)
> +    FIELD(UDC_ISR, EP0_IN_ACK,              3, 1)
> +    FIELD(UDC_ISR, EP0_OUT_ACK,             1, 1)
> +    FIELD(UDC_ISR, EP0_SETUP,               0, 1)
> +REG32(UDC_EP_ACK_IER, 0x10)
> +REG32(UDC_EP_NAK_IER, 0x14)
> +REG32(UDC_EP_ACK_ISR, 0x18)
> +REG32(UDC_EP_NAK_ISR, 0x1C)
> +REG32(UDC_DEV_RESET, 0x20)
> +    FIELD(UDC_DEV_RESET, EP_POOL,           9, 1)
> +    FIELD(UDC_DEV_RESET, DMA,               8, 1)
> +    FIELD(UDC_DEV_RESET, ROOT,              0, 1)
> +REG32(UDC_STS, 0x24)
> +    FIELD(UDC_STS, HIGHSPEED,              27, 1)
> +REG32(UDC_EP0_CTRL, 0x30)
> +    FIELD(UDC_EP0_CTRL, RX_LEN,            16, 7)
> +    FIELD(UDC_EP0_CTRL, TX_LEN,             8, 7)
> +    FIELD(UDC_EP0_CTRL, RX_RDY,             2, 1)
> +    FIELD(UDC_EP0_CTRL, TX_RDY,             1, 1)
> +    FIELD(UDC_EP0_CTRL, STALL,              0, 1)
> +REG32(UDC_EP0_DATA_BUFF, 0x34)
> +/* EP0 SETUP packet buffer: SETUP0 = bytes 0...3, SETUP1 = bytes 4...7 */
> +REG32(UDC_SETUP0, 0x80)
> +REG32(UDC_SETUP1, 0x84)
> +
> +/* Per programmable-endpoint registers (offset from the EP register base) */
> +REG32(EP_CONFIG, 0x00)
> +    FIELD(EP_CONFIG, MAX_PKT,              16, 10)
> +    FIELD(EP_CONFIG, EP_NUM,                8, 4)
> +    FIELD(EP_CONFIG, DIR_OUT,               4, 1)
> +    FIELD(EP_CONFIG, ENABLE,                0, 1)
> +REG32(EP_DMA_CTRL, 0x04)
> +    FIELD(EP_DMA_CTRL, PROC_STS,            4, 4)
> +    FIELD(EP_DMA_CTRL, DESC_OP_EN,          0, 1)
> +REG32(EP_DMA_BUFF, 0x08)
> +REG32(EP_DMA_STS, 0x0C)
> +    FIELD(EP_DMA_STS, PKT_SIZE,            16, 11)
> +    FIELD(EP_DMA_STS, RPTR,                 8, 8)
> +    FIELD(EP_DMA_STS, WPTR,                 0, 8)
> +
> +static void aspeed_udc_update_irq(AspeedUDCState *s)
> +{
> +    bool level;
> +
> +    level = (s->regs[R_UDC_ISR] & s->regs[R_UDC_IER]) ||
> +            (s->regs[R_UDC_EP_ACK_ISR] & s->regs[R_UDC_EP_ACK_IER]) ||
> +            (s->regs[R_UDC_EP_NAK_ISR] & s->regs[R_UDC_EP_NAK_IER]);
> +
> +    trace_aspeed_udc_irq(s->regs[R_UDC_ISR], s->regs[R_UDC_IER], level);
> +    qemu_set_irq(s->irq, level);
> +}
> +
> +static uint64_t aspeed_udc_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    AspeedUDCState *s = ASPEED_UDC(opaque);
> +    uint32_t reg = offset >> 2;
> +    uint32_t val;
> +
> +    val = s->regs[reg];
> +    trace_aspeed_udc_read(offset, val);
> +
> +    return val;
> +}
> +
> +static void aspeed_udc_write(void *opaque, hwaddr offset, uint64_t data,
> +                             unsigned size)
> +{
> +    AspeedUDCState *s = ASPEED_UDC(opaque);
> +    uint32_t reg = offset >> 2;
> +    uint32_t val = data;
> +
> +    trace_aspeed_udc_write(offset, val);
> +
> +    switch (reg) {
> +    case R_UDC_IER:
> +    case R_UDC_EP_ACK_IER:
> +    case R_UDC_EP_NAK_IER:
> +        s->regs[reg] = val;
> +        aspeed_udc_update_irq(s);
> +        break;
> +    case R_UDC_ISR:
> +    case R_UDC_EP_ACK_ISR:
> +    case R_UDC_EP_NAK_ISR:
> +        s->regs[reg] &= ~val;
> +        aspeed_udc_update_irq(s);
> +        break;
> +    default:
> +        s->regs[reg] = val;
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps aspeed_udc_ops = {
> +    .read = aspeed_udc_read,
> +    .write = aspeed_udc_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static uint64_t aspeed_udc_ep_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    AspeedUDCEP *e = opaque;
> +    uint32_t reg = offset >> 2;
> +    uint32_t val;
> +
> +    val = e->regs[reg];
> +    trace_aspeed_udc_ep_read(e->index, offset, val);
> +
> +    return val;
> +}
> +
> +static void aspeed_udc_ep_write(void *opaque, hwaddr offset, uint64_t data,
> +                                unsigned size)
> +{
> +    AspeedUDCEP *e = opaque;
> +    uint32_t reg = offset >> 2;
> +
> +    trace_aspeed_udc_ep_write(e->index, offset, data);
> +    e->regs[reg] = data;
> +}
> +
> +static const MemoryRegionOps aspeed_udc_ep_ops = {
> +    .read = aspeed_udc_ep_read,
> +    .write = aspeed_udc_ep_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void aspeed_udc_reset_hold(Object *obj, ResetType type)
> +{
> +    AspeedUDCState *s = ASPEED_UDC(obj);
> +    int i;
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +    for (i = 0; i < ASPEED_UDC_NUM_EP; i++) {
> +        memset(s->ep[i].regs, 0, sizeof(s->ep[i].regs));
> +    }
> +
> +    /* Device-reset default: root, DMA and EP-pool soft-reset bits set */
> +    s->regs[R_UDC_DEV_RESET] = (R_UDC_DEV_RESET_ROOT_MASK |
> +                                R_UDC_DEV_RESET_DMA_MASK |
> +                                R_UDC_DEV_RESET_EP_POOL_MASK);
> +}
> +
> +static void aspeed_udc_realize(DeviceState *dev, Error **errp)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    AspeedUDCState *s = ASPEED_UDC(dev);
> +    int i;
> +
> +    memory_region_init(&s->udc_container, OBJECT(s), TYPE_ASPEED_UDC,
> +                       ASPEED_UDC_MEM_SIZE);
> +    memory_region_init_io(&s->root_mr, OBJECT(s), &aspeed_udc_ops, s,
> +                          TYPE_ASPEED_UDC ".root",
> +                          ASPEED_UDC_ROOT_NR_REGS << 2);
> +    memory_region_add_subregion(&s->udc_container, 0, &s->root_mr);
> +
> +    /* Each programmable endpoint has its own register bank */
> +    for (i = 0; i < ASPEED_UDC_NUM_EP; i++) {
> +        g_autofree char *name = g_strdup_printf(TYPE_ASPEED_UDC ".ep%d", i);
> +
> +        s->ep[i].index = i;
> +        memory_region_init_io(&s->ep[i].mr, OBJECT(s), &aspeed_udc_ep_ops,
> +                              &s->ep[i], name, ASPEED_UDC_EP_NR_REGS << 2);
> +        memory_region_add_subregion(&s->udc_container,
> +                                    ASPEED_UDC_EP_REG_BASE +
> +                                    i * (ASPEED_UDC_EP_NR_REGS << 2),
> +                                    &s->ep[i].mr);
> +    }
> +
> +    sysbus_init_mmio(sbd, &s->udc_container);
> +    sysbus_init_irq(sbd, &s->irq);
> +}
> +
> +static void aspeed_udc_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +    dc->desc = "ASPEED USB Device Controller";
> +    dc->realize = aspeed_udc_realize;
> +    rc->phases.hold = aspeed_udc_reset_hold;
> +}
> +
> +static const TypeInfo aspeed_udc_types[] = {
> +    {
> +        .name          = TYPE_ASPEED_UDC,
> +        .parent        = TYPE_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(AspeedUDCState),
> +        .class_init    = aspeed_udc_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(aspeed_udc_types)
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 260d2f0751..146ed1a0be 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -536,6 +536,7 @@ config ASPEED_SOC
>       imply GENERIC_LOADER
>       imply PCI_DEVICES
>       imply E1000E_PCI_EXPRESS
> +    select ASPEED_UDC
>       select DS1338
>       select FTGMAC100
>       select I2C
> diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
> index de95686720..e4df15fe7c 100644
> --- a/hw/usb/Kconfig
> +++ b/hw/usb/Kconfig
> @@ -146,3 +146,6 @@ config XLNX_USB_SUBSYS
>   config USB_CHIPIDEA
>       bool
>       select USB_EHCI_SYSBUS
> +
> +config ASPEED_UDC
> +    bool
> diff --git a/hw/usb/meson.build b/hw/usb/meson.build
> index ba55c28ef6..d4ba60a91c 100644
> --- a/hw/usb/meson.build
> +++ b/hw/usb/meson.build
> @@ -27,6 +27,7 @@ system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c'))
>   system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c'))
>   system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
>   system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c'))
> +system_ss.add(when: 'CONFIG_ASPEED_UDC', if_true: files('aspeed-udc.c'))
>   
>   system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
>   system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c'))
> diff --git a/hw/usb/trace-events b/hw/usb/trace-events
> index 67249d69c2..ed05304520 100644
> --- a/hw/usb/trace-events
> +++ b/hw/usb/trace-events
> @@ -377,3 +377,10 @@ canokey_handle_data_out(uint8_t ep_out, uint32_t out_len) "ep %d len %d"
>   canokey_handle_data_in(uint8_t ep_in, uint32_t in_len) "ep %d len %d"
>   canokey_realize(void)
>   canokey_unrealize(void)
> +
> +# aspeed-udc.c
> +aspeed_udc_read(uint64_t offset, uint32_t value) "offset 0x%" PRIx64 " value 0x%x"
> +aspeed_udc_write(uint64_t offset, uint32_t value) "offset 0x%" PRIx64 " value 0x%x"
> +aspeed_udc_ep_read(int ep, uint64_t offset, uint32_t value) "ep %d, offset 0x%" PRIx64 " value 0x%x"
> +aspeed_udc_ep_write(int ep, uint64_t offset, uint32_t value) "ep %d, offset 0x%" PRIx64 " value 0x%x"
> +aspeed_udc_irq(uint32_t isr, uint32_t ier, int level) "isr 0x%x, ier 0x%x, level %d"

Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.



  reply	other threads:[~2026-09-01  6:18 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  2:44 [PATCH v4 0/4] hw/usb: Add ASPEED USB Device Controller (UDC) Jamin Lin
2026-09-01  2:44 ` [PATCH v4 1/4] hw/usb/aspeed-udc: Add ASPEED UDC device controller Jamin Lin
2026-09-01  6:18   ` Cédric Le Goater [this message]
2026-09-01  2:44 ` [PATCH v4 2/4] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Jamin Lin
2026-09-01  2:44 ` [PATCH v4 3/4] hw/usb/aspeed-udc: Add programmable endpoint DMA transfers Jamin Lin
2026-09-01  6:17   ` Cédric Le Goater
2026-09-01  9:16     ` Jamin Lin
2026-09-01  2:44 ` [PATCH v4 4/4] hw/arm/aspeed_ast2600: Wire up the UDC Jamin Lin
2026-09-01  6:17   ` Cédric Le Goater

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d44087a5-bfd5-4e73-bda8-2fd3d940d16b@kaod.org \
    --to=clg@kaod.org \
    --cc=andrew@codeconstruct.com.au \
    --cc=jamin_lin@aspeedtech.com \
    --cc=joel@jms.id.au \
    --cc=kane_chen@aspeedtech.com \
    --cc=leetroy@gmail.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@oss.qualcomm.com \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=steven_lee@aspeedtech.com \
    --cc=troy_lee@aspeedtech.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.