From nobody Sun Jul 26 11:05:45 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass(p=quarantine dis=none) header.from=aspeedtech.com ARC-Seal: i=2; a=rsa-sha256; t=1783064695; cv=pass; d=zohomail.com; s=zohoarc; b=ek9mpmVpVJl5L2ofAjlDJ1MpOPv+iEulSsQvM7PRPJgwVGhJ/yOy+puifZ8EvnoivXZ0bZ6oTSbdDUG3D5W7lqrjJsWUVY8AbtHcpk/2h+c+ZZdAf6vs6Xn+E0aVA4XxFQYY/ske+YeDctQq76z95vsIStz08k4lZHBdT/N28YM= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783064695; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=4u9uP1qyHSWrFRYDiCcQ0ZnlswzwAXJM23lZJceYmvg=; b=DuCMsV/igw5Mekj8N2fxdZ/hN0cmN9EtiRBeEYnlKF6V2+nsotmP3InoyLfx1NRLPBh4TRUjw+rcFazbI0fawaB7hI5+HZWF+fpwnmU67pfCURyLbiGa71bW92veywofygndtJxndB96TUgK+/x8f4KHl8XFLCV/8jzLKEqzAR0= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783064695325400.63165727569117; Fri, 3 Jul 2026 00:44:55 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYYy-0008Qm-MS; Fri, 03 Jul 2026 03:43:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYYv-0008Pr-Lk; Fri, 03 Jul 2026 03:43:49 -0400 Received: from mail-koreacentralazlp170130006.outbound.protection.outlook.com ([2a01:111:f403:c40f::6] helo=SEYPR02CU001.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYYt-0004IZ-AX; Fri, 03 Jul 2026 03:43:49 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by SEYPR06MB5040.apcprd06.prod.outlook.com (2603:1096:101:54::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.181.10; Fri, 3 Jul 2026 07:43:35 +0000 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3]) by TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3%6]) with mapi id 15.21.0181.010; Fri, 3 Jul 2026 07:43:35 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=qxap2nNPtyLTrY2tzYA1HFq4n40W2D6P6UK1tVfN/ls2I/hS0NnZViQkQBmvs08rwKlPTFqSZrjcuKXax0I8/w22DSeHKJeLWWrUqDEUBsCaMWc4dr9IKju/UQmQkgL60vKWT9qCVll77Vy1iJXxLHuyGWFybk6s1F6zUod/HO0OvO2NTbIKF3D9hFEiOzIlq57WAyAg2sYEQlX39UQJKfBP4HHviYJLZ3ZfqplK5fKGLZFvVvXA90A/6PDgF/YzhfLRBnkYDBVMY4E0OwLGNxYnhHW6cjKmz0Rj1BYeoTvdeT1GWckaZ2px23aGJoadrQr4BuHQh0S9z/SMuXjfRw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=4u9uP1qyHSWrFRYDiCcQ0ZnlswzwAXJM23lZJceYmvg=; b=R50E5iB1xkEr31xp2eHQFalQwdJwcRktLvw2bJOLOqi4DUXZzPDko/F3JUexpdhns7LzOKO9HUw4pLeKKC9o5DCaIRI68t9QXx8kbwZHylP3saNgSQO+Td2wf0sgx8c/VrAsln4lBOLxV2ENWFBzMmtgs3WX5ZMweUuSokTfIIR8zHWqJL2iMrQcol8AfeBc8lcwzSpwvokc+g79WEQGZvJQDxP+GZJ9Qs5DInU+jOaEfaOVWAY4lfv1AE7zNfO7IkcCuF5SZbs4N7YRXMX+LYXpKYulXV591SavmT8TOAgN7oGDpQPjTJGZlb//TdZK+v8hJLmwmynHUb4xQzWayw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=aspeedtech.com; dmarc=pass action=none header.from=aspeedtech.com; dkim=pass header.d=aspeedtech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aspeedtech.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=4u9uP1qyHSWrFRYDiCcQ0ZnlswzwAXJM23lZJceYmvg=; b=ZhmFBOztFhbLRA0XTF9ZNEROq+4o1N9+kkjt2fP7loK7AvniOj4b1Ik8hkmarW8U4yMfeQHHVSEBhrnxbTajGkmJ2ZSVdKhNAwRWZ9qXBukTPMuHJGVEa7Cm3+Bdurfxa/Khu5v46sgVeBlGICLhaX8ziwy2+H+8r/UJgRMAdz3KUP9JeON068uwdyy+gXSvegRtiWVQReNY0E2gLuXFlqt8hYZifWz8B/kTrqRaPu254P+YKTnx2YcAZvObdDnBn7j93dehPXw+LwXTOvsdEbn6xxMMcr9ipl6CERgOY+pfKiYsKv3GKMUfnnClw/0QtnG0vw4WKYcn6zduZK8NFw== From: Jamin Lin To: Paolo Bonzini , Peter Maydell , =?iso-8859-1?Q?C=E9dric_Le_Goater?= , Steven Lee , Troy Lee , Kane Chen , Andrew Jeffery , Joel Stanley , "open list:ARM TCG CPUs" , "open list:All patches CC here" CC: Jamin Lin , Troy Lee Subject: [PATCH v1 1/3] hw/usb/aspeed-udc: Add ASPEED UDC device controller Thread-Topic: [PATCH v1 1/3] hw/usb/aspeed-udc: Add ASPEED UDC device controller Thread-Index: AQHdCr+nGAvHtrLIiEy1KPuFrHkplQ== Date: Fri, 3 Jul 2026 07:43:34 +0000 Message-ID: <20260703074332.1049473-2-jamin_lin@aspeedtech.com> References: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> Accept-Language: zh-TW, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=aspeedtech.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: TYZPR06MB4980:EE_|SEYPR06MB5040:EE_ x-ms-office365-filtering-correlation-id: 7d6d87bd-530b-460c-ad18-08ded8d6c992 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|23010399003|376014|366016|1800799024|921020|38070700021|18002099003|22082099003|3023799007|56012099006|6133799003; x-microsoft-antispam-message-info: KIf9D1rt5aKwgA7UXeh463o4EHJAnQODRM44zMdmKEJCSquIssWMk7KCHhMsiWYIHQ+w+ecOXvoOMCmTyH560y7Vx58ARBWZ3l5fL+pnHM5/gywSrRaF6RjWN3EGFBKpNBo1xx5YRJchLJBDxwIsN17FYzfmJALKuf79H0jtXXzXV+4yIE4+nzb2gA5TX8CksRr/IEFXO5tGH8JdmsTHiNaQiwcfc7cgTfThtO7C5ez+gHSb53GsEReLFAykVXejXfxo1mDRUCnjEEysVPaGnfI3p7EFSeau0b4A3odTvfSXBkpd1R3TpYCCed6Oa135LXrsFEuqCYDeg5LMXwNptDXKjQK+5AmpwG0Xvc2zc9zic/iXtaYlArvxpHJ/eUWKNEAGWUdEv2pkrWsemjDth/BLZAnWw//yokNMg8uFkjgNyQpd/QD0qrhkg/Ml/myJHLvf7SGjVfoQhsM96SNH8ff2L/Utk+MmwGWGG5PRUYUE5TSFfnXJQUpgQXHkVViyZlW5enjuqB2M0meeBdSKivHpLkq9cz3/5UOKtMlsDCWXgjt0H2+pR4D8pUXJBgCFS8hi55aNzVxNmahJtZ2OH6YLndIHjVg3fJcpup11CISLUhvcpzJiA8M38L6UOXXSK6ZJ8MZyTccaYH7+kaREGbtLxakQEt054YzbenDrPka2dhaKSGtNF1zj9UuHXHjtEEXzle59Xjx4FQVmpB530H9tJl0KERGfYeHvrmQys9gjgr6A+DsB1HILH2goUnf7 x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:TYZPR06MB4980.apcprd06.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(23010399003)(376014)(366016)(1800799024)(921020)(38070700021)(18002099003)(22082099003)(3023799007)(56012099006)(6133799003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?opUvqVcHNHYnpbgm47qdMgCQbL3zZWBeKnb0VjLspPCXtMVZk2zPX344or?= =?iso-8859-1?Q?Ddi0u9+9EjG+fQZgn9FvfMs6dAXTm6j2RfMTWxvY81g20Wsg3/ntHEs/eQ?= =?iso-8859-1?Q?w5yV7w1D6GtaF2NgbQf5wCxx5r40d0CKm40B0CvkXyl55h9FiI5uhGJFBY?= =?iso-8859-1?Q?HTOA+y9ueYrBNODEU3mHJhPHlhhO6VX/ndqAvNkOFiy0KMiIN68pT/k9nj?= =?iso-8859-1?Q?euNwYP6fiVSZKb8dxEy+0PjIvGK4SqpzUYwO/ULo8Zwg/gNSh/qbv4jFR2?= =?iso-8859-1?Q?tyxv0ixMnlF63SadfUfJEcuMT2XBc/EKKzY2Xxy1fwBonOdjZ6ZeQtWdDz?= =?iso-8859-1?Q?Eb3YHeHsguNoE67tJT+ZgNbNqq8cxfcJEcg2g0JP/I3Y1JuY8KId4+kGEL?= =?iso-8859-1?Q?4WBnT1c5s3YqfZPmoFJScagQPYQ/xHMix4r0zm8x86aEPvnnpm1TBqCyse?= =?iso-8859-1?Q?WfXFx6BPEk4gt9Y3xgkHOtl141F4Quqjw/WADNw9kdMwnx+pFVBnmBgMdY?= =?iso-8859-1?Q?hmq/rzjtLUHuwSrpYACknZMXJIRFCHHW1uisXPTfxocoUBMm2WMbTQdiwl?= =?iso-8859-1?Q?K9/HVa5vjR7dtWOVzjpDZQqD9KbZJM93LOO+um+5NgI7oyWSA96tecOAUe?= =?iso-8859-1?Q?VH7dESBbf+qLuZ4G7e48rpkiC3snn7QTo34zGhc7Jb04Knh6Y8FBZSnFAt?= =?iso-8859-1?Q?q7GlL2NlHKZQZ0tD7zfjAC2+zPRniwAg+hiZ3vJZHQnGcHYQfXWGlbuW9z?= =?iso-8859-1?Q?PJ1hBfhioBo9wvBl9/YvbJAWXB51Q8gO5CMAEoXYpL8hfi6DNyV+oTQUQT?= =?iso-8859-1?Q?jA48ecvJxDCRhYhd9z2ovhA8PjGCeVI2Qws5BvI7+6XcOjoj3u6whmpthY?= =?iso-8859-1?Q?mWCxD+AI72X0BX59z95iQzWU7XAwYZlhY/d88zB0p+l7tehX9RwZkw1XH7?= =?iso-8859-1?Q?hzVqEuSS5uuaIema8qdSynoKp/228D7SA+1T3+DnDeHAmf9WwV4frrfqT7?= =?iso-8859-1?Q?bcZRMuenKYKosAh31AXypODcw5vitcC3ecprrCUBm09vWJh0K812IvvU7D?= =?iso-8859-1?Q?ctiHRAfeR0QVO3IVjw4iDz4ZZ4tVE+f1f9cM8fosoivXgrXJqa9NGDl/6e?= =?iso-8859-1?Q?zeW8D2WCoz693njhMOOxtlxmGE0KzNlWSktnjrvXxHAmAWSiuCI3CLA+vw?= =?iso-8859-1?Q?VSWdTQKB7F+/PlieYrEGqxNOSQZP6ks6DUIR+EThx7naSQDPepNqDqrW3D?= =?iso-8859-1?Q?ESIGO3cKZ9bgrQONsw95Tnh/JslxpKItuHVmAQj2fan7PmLLRQ+AKFvXyz?= =?iso-8859-1?Q?DStLVqcRsL3f7N4o5l9UuzmVxfe16t4hYTOE4ee4/x7HFFLPIdzbBJzfn+?= =?iso-8859-1?Q?AZ8w+R+HOQo78JIMRc1dM+w8uY7baV0SS/kNgx8CBX+mH+TynYsvEMw7e5?= =?iso-8859-1?Q?gTFsmd1P4/k2ZbwlwiLBC/k+sRvYNZDmRNFQ7upOKNKV/7wablI94xa9XR?= =?iso-8859-1?Q?NWXSVwwrKYc4cnEEshJRK3l44hXL/zcCDIEuDxqdroSiliDcYyh/15B2PQ?= =?iso-8859-1?Q?+1rNpRMRAz3uh3YX9UgpSvkEb1q4fi6q0dFVTnDgCRrFysHjPbCgt0M6fn?= =?iso-8859-1?Q?brqcax+NZqPHSaQojodZE82xv54gMymFEnmYdt6M0dRJWzjCNpBEUtncNg?= =?iso-8859-1?Q?Zu2yCi8Zpn/QM+kAc/wxlCPrbBQWzLJUGXQOtXb/plE6J4+YFF7F1aQHuA?= =?iso-8859-1?Q?AEVeH9dMq8lxkpUvzdF4FExu0NNY0Z2en09RHx4ecP1phKmoVcDETVpE12?= =?iso-8859-1?Q?fb2zbOVr1w=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: D8mNLK9vqFZ/nWuGQ93xSemq8q1UegabtI3RLNn6mInEuI9ONVZZkhLqVC6YqZgI5P+nNIjNCW7cZ2NIblstBA7aPNa72/awpWR9rkZVxDm4n7AiBofqY/88N/Swve3uGxubdi1Y13ZjVVmWq8Rmd0uEX4RQksFAZwaV1firVIBqwCdARjY/3gTc9zlbcnJ3hDR2S36KjOw1b9WCO6kyVPtvpOY7VlurWeKGdIbLoivvnRvaryXHv38MzarWb+6YWUK+l+3TfbU8FzIWDii90MQ6GrJfQuuZMdhfyMJAbTysvs6gi+i3y9qm8akzIGKkGp/Y5zLpVC3ZzzyyIn7ZKw== X-OriginatorOrg: aspeedtech.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: TYZPR06MB4980.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7d6d87bd-530b-460c-ad18-08ded8d6c992 X-MS-Exchange-CrossTenant-originalarrivaltime: 03 Jul 2026 07:43:34.9269 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 43d4aa98-e35b-4575-8939-080e90d5a249 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: tUVeS+BcpG/DCh4EBj8f/QIEj5VrJv4GvHZxWbuLSxldqaOri9+Xt3MiRtT1aMnZLiQHQ4MDenARVGIzsSiLb5DPRazCozIEDd78Gx/p3us= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SEYPR06MB5040 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a01:111:f403:c40f::6; envelope-from=jamin_lin@aspeedtech.com; helo=SEYPR02CU001.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @aspeedtech.com) X-ZM-MESSAGEID: 1783064695642158500 Content-Type: text/plain; charset="utf-8" The AST2600 has a USB 2.0 Device Controller (UDC) at 0x1e6a2000 with one control endpoint and four programmable endpoints, driven by the Linux "aspeed_udc" gadget driver. 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_DEV) that a host controller enumerates, which is added in the next patch. Signed-off-by: Jamin Lin --- hw/arm/Kconfig | 1 + hw/usb/Kconfig | 4 + hw/usb/aspeed-udc.c | 264 ++++++++++++++++++++++++++++++++++++ hw/usb/meson.build | 1 + hw/usb/trace-events | 7 + include/hw/usb/aspeed-udc.h | 54 ++++++++ 6 files changed, 331 insertions(+) create mode 100644 hw/usb/aspeed-udc.c create mode 100644 include/hw/usb/aspeed-udc.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index fb798ccbee..8fcf9f48c9 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -532,6 +532,7 @@ config ASPEED_SOC default y depends on TCG && ARM imply PCI_DEVICES + select ASPEED_UDC select DS1338 select FTGMAC100 select I2C diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index de95686720..e8c00f813a 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -146,3 +146,7 @@ config XLNX_USB_SUBSYS config USB_CHIPIDEA bool select USB_EHCI_SYSBUS + +config ASPEED_UDC + bool + select USB diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c new file mode 100644 index 0000000000..9be9cfcf13 --- /dev/null +++ b/hw/usb/aspeed-udc.c @@ -0,0 +1,264 @@ +/* + * 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), driven by the Linux + * "aspeed_udc" gadget driver. It implements one control endpoint (EP0) and + * 4 programmable endpoints. + * + * This file is the system-bus side of the controller: the MMIO register m= ap, + * 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) + FIELD(UDC_FUNC_CTRL, UPSTREAM_FS, 1, 1) + FIELD(UDC_FUNC_CTRL, STOP_CLK_SUSPEND, 2, 1) + FIELD(UDC_FUNC_CTRL, AUTO_REMOTE_WKUP, 3, 1) + FIELD(UDC_FUNC_CTRL, REMOTE_WKUP_EN, 4, 1) + FIELD(UDC_FUNC_CTRL, TEST_MODE, 8, 3) + FIELD(UDC_FUNC_CTRL, PHY_RESET_DIS, 11, 1) + FIELD(UDC_FUNC_CTRL, EP_LONG_DESC, 18, 1) + FIELD(UDC_FUNC_CTRL, PHY_CLK_EN, 31, 1) +REG32(UDC_CONFIG, 0x04) + FIELD(UDC_CONFIG, DEV_ADDR, 0, 7) +REG32(UDC_IER, 0x08) +REG32(UDC_ISR, 0x0C) + FIELD(UDC_ISR, EP0_SETUP, 0, 1) + FIELD(UDC_ISR, EP0_OUT_ACK, 1, 1) + FIELD(UDC_ISR, EP0_OUT_NAK, 2, 1) + FIELD(UDC_ISR, EP0_IN_ACK, 3, 1) + FIELD(UDC_ISR, EP0_IN_NAK, 4, 1) + FIELD(UDC_ISR, BUS_RESET, 6, 1) + FIELD(UDC_ISR, SUSPEND, 7, 1) + FIELD(UDC_ISR, RESUME, 8, 1) + FIELD(UDC_ISR, EP_POOL_ACK, 16, 1) + FIELD(UDC_ISR, EP_POOL_NAK, 17, 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, ROOT, 0, 1) + FIELD(UDC_DEV_RESET, DMA, 8, 1) + FIELD(UDC_DEV_RESET, EP_POOL, 9, 1) +REG32(UDC_STS, 0x24) + FIELD(UDC_STS, HIGHSPEED, 27, 1) +REG32(UDC_EP_DATA, 0x28) +REG32(UDC_ISO_TX_FAIL, 0x2C) +REG32(UDC_EP0_CTRL, 0x30) + FIELD(UDC_EP0_CTRL, STALL, 0, 1) + FIELD(UDC_EP0_CTRL, TX_RDY, 1, 1) + FIELD(UDC_EP0_CTRL, RX_RDY, 2, 1) + FIELD(UDC_EP0_CTRL, TX_LEN, 8, 7) + FIELD(UDC_EP0_CTRL, RX_LEN, 16, 7) +REG32(UDC_EP0_DATA_BUFF, 0x34) +/* EP0 SETUP packet buffer: SETUP0 =3D bytes 0...3, SETUP1 =3D 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, ENABLE, 0, 1) + FIELD(EP_CONFIG, DIR_OUT, 4, 1) + FIELD(EP_CONFIG, TYPE, 5, 2) + FIELD(EP_CONFIG, EP_NUM, 8, 4) + FIELD(EP_CONFIG, STALL, 12, 1) + FIELD(EP_CONFIG, AUTO_TOGGLE_DIS, 13, 1) + FIELD(EP_CONFIG, MAX_PKT, 16, 10) +REG32(EP_DMA_CTRL, 0x04) + FIELD(EP_DMA_CTRL, DESC_OP_EN, 0, 1) + FIELD(EP_DMA_CTRL, SINGLE_STAGE, 1, 1) + FIELD(EP_DMA_CTRL, RESET, 2, 1) + FIELD(EP_DMA_CTRL, IN_LONG_MODE, 3, 1) + FIELD(EP_DMA_CTRL, PROC_STS, 4, 4) +REG32(EP_DMA_BUFF, 0x08) +REG32(EP_DMA_STS, 0x0C) + FIELD(EP_DMA_STS, WPTR, 0, 8) + FIELD(EP_DMA_STS, RPTR, 8, 8) + FIELD(EP_DMA_STS, TX_SIZE, 16, 11) + +/* Device-reset default: root, DMA and EP-pool soft-reset bits set (0x301)= */ +#define UDC_DEV_RESET_DEFAULT \ + (R_UDC_DEV_RESET_ROOT_MASK | R_UDC_DEV_RESET_DMA_MASK | \ + R_UDC_DEV_RESET_EP_POOL_MASK) + +static void aspeed_udc_update_irq(AspeedUDCState *s) +{ + bool level; + + level =3D (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 =3D ASPEED_UDC(opaque); + uint64_t val =3D s->regs[offset >> 2]; + + trace_aspeed_udc_read(offset, val); + return val; +} + +static void aspeed_udc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedUDCState *s =3D ASPEED_UDC(opaque); + uint32_t reg =3D offset >> 2; + uint32_t val =3D data; + + trace_aspeed_udc_write(offset, val); + + switch (reg) { + case R_UDC_ISR: + case R_UDC_EP_ACK_ISR: + case R_UDC_EP_NAK_ISR: + /* Status registers are write-1-to-clear */ + s->regs[reg] &=3D ~val; + break; + default: + s->regs[reg] =3D val; + break; + } + + aspeed_udc_update_irq(s); +} + +static const MemoryRegionOps aspeed_udc_ops =3D { + .read =3D aspeed_udc_read, + .write =3D aspeed_udc_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 4, + }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +static uint64_t aspeed_udc_ep_read(void *opaque, hwaddr offset, unsigned s= ize) +{ + AspeedUDCEP *e =3D opaque; + uint64_t val =3D e->regs[offset >> 2]; + + 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 =3D opaque; + + trace_aspeed_udc_ep_write(e->index, offset, data); + e->regs[offset >> 2] =3D data; +} + +static const MemoryRegionOps aspeed_udc_ep_ops =3D { + .read =3D aspeed_udc_ep_read, + .write =3D aspeed_udc_ep_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 4, + }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +static void aspeed_udc_realize(DeviceState *dev, Error **errp) +{ + AspeedUDCState *s =3D ASPEED_UDC(dev); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); + int i; + + s->regs =3D g_new0(uint32_t, ASPEED_UDC_NR_REGS); + + memory_region_init(&s->iomem, OBJECT(s), TYPE_ASPEED_UDC, + ASPEED_UDC_REG_SIZE); + + /* Root/global registers occupy the low part of the window */ + memory_region_init_io(&s->reg_mr, OBJECT(s), &aspeed_udc_ops, s, + TYPE_ASPEED_UDC ".regs", ASPEED_UDC_NR_REGS << 2= ); + memory_region_add_subregion(&s->iomem, 0, &s->reg_mr); + + /* Each programmable endpoint has its own register bank */ + for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { + g_autofree char *name =3D g_strdup_printf(TYPE_ASPEED_UDC ".ep%d",= i); + + s->ep[i].index =3D i; + s->ep[i].regs =3D g_new0(uint32_t, ASPEED_UDC_EP_NR_REGS); + 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->iomem, ASPEED_UDC_EP_REG_BASE + + i * ASPEED_UDC_EP_REG_SIZE, &s->ep[i].= mr); + } + + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void aspeed_udc_reset_hold(Object *obj, ResetType type) +{ + AspeedUDCState *s =3D ASPEED_UDC(obj); + int i; + + memset(s->regs, 0, ASPEED_UDC_NR_REGS * sizeof(uint32_t)); + for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { + memset(s->ep[i].regs, 0, ASPEED_UDC_EP_NR_REGS * sizeof(uint32_t)); + } + s->regs[R_UDC_DEV_RESET] =3D UDC_DEV_RESET_DEFAULT; +} + +static void aspeed_udc_unrealize(DeviceState *dev) +{ + AspeedUDCState *s =3D ASPEED_UDC(dev); + int i; + + for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { + g_free(s->ep[i].regs); + } + g_free(s->regs); +} + +static void aspeed_udc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->desc =3D "ASPEED USB Device Controller"; + dc->realize =3D aspeed_udc_realize; + dc->unrealize =3D aspeed_udc_unrealize; + rc->phases.hold =3D aspeed_udc_reset_hold; +} + +static const TypeInfo aspeed_udc_types[] =3D { + { + .name =3D TYPE_ASPEED_UDC, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AspeedUDCState), + .class_init =3D aspeed_udc_class_init, + }, +}; + +DEFINE_TYPES(aspeed_udc_types) 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')) =20 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 0d4318dcf1..fb79e24fba 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_l= en) "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, uint64_t value) "offset 0x%" PRIx64 " val= ue 0x%" PRIx64 +aspeed_udc_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " va= lue 0x%" PRIx64 +aspeed_udc_ep_read(int ep, uint64_t offset, uint64_t value) "ep %d, offset= 0x%" PRIx64 " value 0x%" PRIx64 +aspeed_udc_ep_write(int ep, uint64_t offset, uint64_t value) "ep %d, offse= t 0x%" PRIx64 " value 0x%" PRIx64 +aspeed_udc_irq(uint32_t isr, uint32_t ier, int level) "isr 0x%x, ier 0x%x,= level %d" diff --git a/include/hw/usb/aspeed-udc.h b/include/hw/usb/aspeed-udc.h new file mode 100644 index 0000000000..eb279dd9c3 --- /dev/null +++ b/include/hw/usb/aspeed-udc.h @@ -0,0 +1,54 @@ +/* + * 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) + +/* + * 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 +/* 32-bit registers per programmable endpoint */ +#define ASPEED_UDC_EP_NR_REGS 4 + +/* + * The root/global register block spans 0x000...0x087: the SETUP data buff= er + * ends at 0x84. Size the backing array to cover the whole block. + */ +#define ASPEED_UDC_NR_REGS (0x88 >> 2) + +/* MMIO window: root registers below EP_REG_BASE, then the per-EP banks */ +#define ASPEED_UDC_REG_SIZE 0x300 +#define ASPEED_UDC_EP_REG_BASE 0x200 +#define ASPEED_UDC_EP_REG_SIZE 0x10 + +typedef struct AspeedUDCEP { + MemoryRegion mr; + int index; + uint32_t *regs; +} AspeedUDCEP; + +struct AspeedUDCState { + SysBusDevice parent_obj; + + /* container: root registers + per-endpoint banks */ + MemoryRegion iomem; + MemoryRegion reg_mr; + qemu_irq irq; + uint32_t *regs; + AspeedUDCEP ep[ASPEED_UDC_NUM_EP]; +}; + +#endif /* HW_USB_ASPEED_UDC_H */ --=20 2.53.0 From nobody Sun Jul 26 11:05:45 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass(p=quarantine dis=none) header.from=aspeedtech.com ARC-Seal: i=2; a=rsa-sha256; t=1783064704; cv=pass; d=zohomail.com; s=zohoarc; b=FN7ngCMlM3UUS9V27PxYA8O2y4koD4Hn8NZkEleiJiXSk/7oE50SLo23oCfFGaSYM01siAdVegx7S9S4DvDG+R6xokPSZbWmWwH/ZTvnYVxLcWre+3DLwNELEF7guNFTBjr09mibTbTYA88QmCclE0sN/70ZmDdFAO3Ep0U+LJA= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783064704; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=R+h2GRZ3xxPsPO1dXKa/rMEySOYcbepV89G6qzZJD1M=; b=A1reljNbSaC7DDgY6wYWe9BT0qUiyosh5CN/tNMgXfOazz3zqg0ZjJT7+E4ONzXne/Icikp2XKKmnSJOFzzAE2DVKozYqUJoTmu9YTqLg6toeuvuTKStYBwjvOyAE0BqFHoHMn+iyb0TLlVMoyYQCXVq6D/e/jLon1w/G86vP7s= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783064704371460.2580011549221; Fri, 3 Jul 2026 00:45:04 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYZ1-0008Ri-LW; Fri, 03 Jul 2026 03:43:55 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYZ0-0008RL-6x; Fri, 03 Jul 2026 03:43:54 -0400 Received: from mail-koreacentralazlp170130006.outbound.protection.outlook.com ([2a01:111:f403:c40f::6] helo=SEYPR02CU001.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYYw-0004IZ-K2; Fri, 03 Jul 2026 03:43:53 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by SEYPR06MB5040.apcprd06.prod.outlook.com (2603:1096:101:54::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.181.10; Fri, 3 Jul 2026 07:43:36 +0000 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3]) by TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3%6]) with mapi id 15.21.0181.010; Fri, 3 Jul 2026 07:43:36 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=uq82XDjoBZQizwlKxz1o8j0w3CJOumHU0XqKMzukoVsa46vrIDKcw7ote1UEtxUXZ0iH7C/ua3PvMAgVZEfNV1FXOiR3+6uiX6yAAmntUg7x7yDcrsmrpqH/+WBgKJxZBYxXsJg/NUPM9UWPz1tSsy66VJZpNVoQhq1F7fv8yHnQtqdHmzwmqjsFOOxeK9ATnNSvKISl+ibnXj6NmVmcht2wooiKMZsb7gTvVnANwUi+FxNSTGJusPnHSon8WpDj03BjxLGd6QjM6Gcr8nyaAOXIY5JZifvbC42dE2W9HvovWwJh/Erb0z6Xv33YlcNvEU5aMBSN6WXj2LvmIDV3Sw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=R+h2GRZ3xxPsPO1dXKa/rMEySOYcbepV89G6qzZJD1M=; b=K7A72ZZHunS3JveEG2UAaVtXrXgQzac4vfRV8KE2eZpYfg+KaCrXDotlikApbY6MA7adD45G8iTRi42qrVafDagCQXIQpVks5H6qCLdcrxYyIFJLhOx8P5C9al3WIr/g4lwwNex/TNpwpHM8ducSWj/xC2O3AoJP6YXO1T4qXj+fjvfLbzQAqFKmriw1sWLAXruLcoIWkkK1EWwK8gfeCZ05j2leC13xoN1hGtUD3Hin8AUvYG23bwIhdSEJnmtqjHHm6vYcXXnqHc3CJ0CVX9nJdqbAKKtWC1NywAmMPlE2gU4R5PWTX+hC4bOlmEVSoz0fbrcSp0/BHN/8Eo3CcQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=aspeedtech.com; dmarc=pass action=none header.from=aspeedtech.com; dkim=pass header.d=aspeedtech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aspeedtech.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=R+h2GRZ3xxPsPO1dXKa/rMEySOYcbepV89G6qzZJD1M=; b=aE6RYSe8WnQO7bbfmrF6zScVWX+oZKnE2cILdw7bfuy4tMUz7RuWI1z2CmQnXhAjeHVGD1LYD1JYaWLdsGgVO9EXFAWxqvxQ5D1nbk3iEIF6F8FPEynZcAMpIG4AUuHmBLoas8HHZBcOV3Gx9kbP+AnMokOWFQ9FiLMKUJ8STLzJQEzHaQSnNkT/f5m2IsQhQHUaPUJ+nqnrK3CaO0Ux2l/4CBfdwPVLd9IZk8Cq4P+b1f9qUXJdrISJR958scsTCZB+2g0cihecb+Ws4B3z5w9SGphB34aVPc4ICG729bp+iOHzydLBYHQctZ603Afifof3BLnNVkBIFCGLoXSwNA== From: Jamin Lin To: Paolo Bonzini , Peter Maydell , =?iso-8859-1?Q?C=E9dric_Le_Goater?= , Steven Lee , Troy Lee , Kane Chen , Andrew Jeffery , Joel Stanley , "open list:ARM TCG CPUs" , "open list:All patches CC here" CC: Jamin Lin , Troy Lee Subject: [PATCH v1 2/3] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Thread-Topic: [PATCH v1 2/3] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Thread-Index: AQHdCr+nyhYtVwGwzUqRRDy7oe8CXQ== Date: Fri, 3 Jul 2026 07:43:36 +0000 Message-ID: <20260703074332.1049473-3-jamin_lin@aspeedtech.com> References: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> Accept-Language: zh-TW, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=aspeedtech.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: TYZPR06MB4980:EE_|SEYPR06MB5040:EE_ x-ms-office365-filtering-correlation-id: 07edb5a3-da38-4327-9c8a-08ded8d6ca55 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|23010399003|376014|366016|1800799024|921020|38070700021|18002099003|22082099003|3023799007|5023799004|56012099006|6133799003; x-microsoft-antispam-message-info: G654ElE8VG574sBxU8ZErQ6jlOyDEzJVNlwwnM96KBeRrb6R7/hQmeig5v9ao9Pal5M8xTcjgN/grZaqH+BBrb5bk9J6ZhCEKiV0GNp3dfcGiLfq0RZ+sfK7lwXioPBk2lLEgTqK+vy4HgGDgUH1WtGN/zzCYOetaboPAtNMEj6xMv1jvDnAzVhgu9eN5fm7R0Pr59WpCRpNd+tuWtK5kKJfaLKW4gVd4hcj1YNv/b5KuMhz8ShkrAs0k3kzaWtX/uTudEXwKnSD99elniuRSphH4CknKj4fuoYA+l3kqGPmADvHRjaZtCCneXRRdjwmBebdqJ3VlizOz2mOnEcWlWrE0b31Unlv5CoTxxvcJT6x2RFfnY0c5IVz7WFUfPbsFZ+phncpsGtKbofuXJFc+M+88s9QbxGHyLAyLwNvg91GNuchbpnJffVxUwMi1HOheNJKtZT1SkHUdlQD7PspX285VhT1UvvMGe+1f8FuUsokZUmeno1UwNWcaePux0op/Eq1UvoMDR3hNXZ0ogYvaHXgB4OxzDlvK8cfdfsd2sQkstRnOZAJWT7iVxhcL82OHTTLXJS/50mnGxmgHfZFQo1WRUDp998HMUR8RRkE3Are67Xs1qB8iuxteqjwvTBofovYK+Lvop2SAN0HeWc6QqbZg1OOTooV13btkQ40MH//djYM2GBySQu4HhfPMwChsPGht5tfKCOv6ZJqlFnBLCDnFwCeM2d07lnRH1MQUryUIVdrYcCv3XBCNkd3SCHS x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:TYZPR06MB4980.apcprd06.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(23010399003)(376014)(366016)(1800799024)(921020)(38070700021)(18002099003)(22082099003)(3023799007)(5023799004)(56012099006)(6133799003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?11ZPWtUAIaTTiJrokkvC9XulfeQT5AOiwoOVKZ//Z7xADiSa580ybbImC0?= =?iso-8859-1?Q?WZFgb55BZU1YzWGlskrsI5XjG5ywXF9tLKjqnpxOqqk+hYuUdV45e8e28/?= =?iso-8859-1?Q?LQviz9o0zNhsC9VieaaNwU5EucRv0HdB3aC+GA0wLj9/xGnFifHi2LvDw8?= =?iso-8859-1?Q?mrryXeN7u5N/lXTO2AL+q1FsLQBANlFSKULjEjNW8rSF0ptVKcekWKSZ+u?= =?iso-8859-1?Q?GbYSlfzWu0Jc4pLdbGyzUhz1muQgDUXlYm5mk6+7ttxGHB/ZjArVod6WR8?= =?iso-8859-1?Q?1rqxP1g1RqmFDwvcKUuf5erW/a+2zbqG9Oj0e4kJTrwsCo8bIwQhtjYQlK?= =?iso-8859-1?Q?0x1j9zaM3LxDWh9zdaILIgNUXX4jAjnz7LzoBTaNLdbwjGYQUxyah0+7tN?= =?iso-8859-1?Q?qwqxwoIlE/Vf0Wjk5y6qNAMHT7OnILsy9Zklnv5+kyzCyUaEGPKujMLpDu?= =?iso-8859-1?Q?ng+06oLQu4+gNTp8PRD0ofx52LxwMMjGlLIyiqUe91hd4OKckKjBi39XaQ?= =?iso-8859-1?Q?of2Sjimc6CZLuEVLrhJbDMI6q3FeK0D+J7FrdkiN+q+hJ4127rfpxHHlEx?= =?iso-8859-1?Q?hMXmX5qas14N4ITml2ZaYJLRxi39nBtSUtdyv6pE3CpQwq1St7/oqEKgPp?= =?iso-8859-1?Q?IMgQPVdUob+D89p8NMqfEJa6qrSIZzapVIq0FPDS3mD617nu7qpU7y6nYH?= =?iso-8859-1?Q?KPI6rO7wF5qyB0hzQmviYDHkfUH2ptG9TTMbBjR7/gOCu1GYj1GzwkCjH3?= =?iso-8859-1?Q?C3592tgTkoGbNaIzvMsCDNJbxidCwwUgEzLqcZxVBL4RPAHBLm52g3QLzi?= =?iso-8859-1?Q?QUgaBwttn1W93hT1Kkf/y6iBLfrmAlIEtYA39QyMj7HNxyH8rwBSncwp6Q?= =?iso-8859-1?Q?8OOvRuvQSotOJrTfxvM8z/pe1ZJl8mEHPBGpVUe8320NyQLOXikQpz0q0b?= =?iso-8859-1?Q?YPtip2Jhgeaw+OPzNMXrE9zcYQsVAPnO376tjK+5DPpsB1tQuOthV/ppPM?= =?iso-8859-1?Q?UZf2+c5HCV9pGborQ42J5CSxunTNe4YAPOFhJdH9P0M6hN1G789L039MDF?= =?iso-8859-1?Q?NbMsilThkThAxoHpokQFYs0wVmEY9eg0w26ikrPhq/2YrPjCnWcm6Uk8SG?= =?iso-8859-1?Q?ZZBKBYL1+txlKqEZ0E3RZ6XsvtfpbXG7dBo3wA1jxH/RafztC+roC+yK0z?= =?iso-8859-1?Q?KCNykSLxQc+bnr9oLhxZ3IKBO55EKDsN3QDLuy/MEUL+gUqGt6VKpnpOmz?= =?iso-8859-1?Q?S9P0KJGBjN4/w09LWZCTcZLT+NWwA/Q6q/UpVeZ4VY22nkfW1WlLeIfTBp?= =?iso-8859-1?Q?3/ryvKFesxXCTUxF+wXQBZ5PRit8mtH1TDLGmUslUgbYuHuWUW7G6VTlzR?= =?iso-8859-1?Q?3SzVV5gqIsW4xNvVjXG296P4BjdEJihEPulwOt9rvHjbezZn/HkGe2XZX7?= =?iso-8859-1?Q?aLkIllVw/mHrChsPw7xBd+YhsdRu7iScG1M2suRhZAglEXtkSTYjLlqV4Z?= =?iso-8859-1?Q?kZwcW5MnUH9LRvpxRGSEwTfyD+4kv5zFJNlhKM9SFnjs/Cfif5B/PMlSqp?= =?iso-8859-1?Q?l9mFQ1gi6QkTLG7so1ystXQaqjNBOfYclY7E8MwH1XgyE8CqFfZShjvPLi?= =?iso-8859-1?Q?4leFFWrI0US6Dmb2/yEUEEF6PFDv1HhG9UIiuCnp72JCyA2ksq0L3EOX+d?= =?iso-8859-1?Q?1Cls/NtU3J7r++dRfqdJPKj/w+bhNPgITU2cmNbj2+G1b8oLq6wiXrZr3q?= =?iso-8859-1?Q?ppMnOHEuajV1ZFIORm0nz2skCj7uSE6sMwJbZB7/Bzs7oXCzAM2Az931fO?= =?iso-8859-1?Q?U51aT78rxg=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: U7LsENe7qk23K+26Ay64EQVLwBa9/zrBjxA85bjDTakfIqBPW8R5uhZDy1+3TtFC7r6Jw171Qcsxp3Iy8u8e546TLG46YvgmOozpmkaEvnG1yibPUWKcbKWCSoWEZhc324/w6mYFYUyMW8jAojfKrO/39ikVT1/pbz+CMX/YkQehEGW13BKedeikTNqO7Po/DRY/ksdYSHPcdZuQeAun6oW+98bVd2P/bFpggaMloZhasj2jiJc/JZfJ0bgAc1TbsVr/B8Y3u1vfwNObapdQ0r57L/JPlduWhZrACZXBSsLW0n8vO3/rPfpae/1Y72ryDWsStp14ez/eODqk2L4HpA== X-OriginatorOrg: aspeedtech.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: TYZPR06MB4980.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 07edb5a3-da38-4327-9c8a-08ded8d6ca55 X-MS-Exchange-CrossTenant-originalarrivaltime: 03 Jul 2026 07:43:36.2064 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 43d4aa98-e35b-4575-8939-080e90d5a249 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: Iju7xdNBaxAzPtSHt9u1/IMah/WV/YB/TI5+bhVnLl8pZOiWzkiVJJ5kiyTjwdLa5YNknHGoXThL9sQdmVJkPataP4A59Qf2cSs7uv9TbNk= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SEYPR06MB5040 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a01:111:f403:c40f::6; envelope-from=jamin_lin@aspeedtech.com; helo=SEYPR02CU001.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @aspeedtech.com) X-ZM-MESSAGEID: 1783064705816158500 Content-Type: text/plain; charset="utf-8" Present the UDC gadget side to a USB host controller as a USB device (TYPE_ASPEED_UDC_GADGET). This is a normal QEMU USB device, so it can be attached to any USB host controller bus, not only the BMC's own EHCI. It forwards host transactions to the guest gadget driver by raising the matching controller interrupts, and completes them once the driver answers through the device-controller MMIO registers: - EP0 control: the SETUP packet is mirrored to the SETUP data buffer, and the driver's IN/OUT data is copied to/from the host packet. - Bulk endpoints: IN uses the descriptor-list DMA ring, OUT uses the single-stage DMA buffer. A transfer larger than one host packet is served across several polls and the packet is parked until the gadget queues more data. The gadget connects to / disconnects from the host bus when the driver sets or clears the upstream-enable bit (pull-up), and is detached on reset so a guest reboot does not leave the host enumerating a gadget with no driver behind it. Signed-off-by: Jamin Lin --- hw/usb/aspeed-udc.c | 678 +++++++++++++++++++++++++++++++++++- hw/usb/trace-events | 9 + include/hw/usb/aspeed-udc.h | 30 ++ 3 files changed, 711 insertions(+), 6 deletions(-) diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c index 9be9cfcf13..3d69e33516 100644 --- a/hw/usb/aspeed-udc.c +++ b/hw/usb/aspeed-udc.c @@ -9,18 +9,30 @@ * "aspeed_udc" gadget driver. It implements one control endpoint (EP0) and * 4 programmable endpoints. * - * This file is the system-bus side of the controller: the MMIO register m= ap, - * 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. + * The model has two faces: + * - a SysBus device exposing the MMIO register interface, the interrupt= and + * the integrated DMA engine to the guest gadget driver (the "system b= us + * device" section below); + * - a USBDevice presented on a host controller's bus, which forwards ho= st + * transactions to the gadget by raising the matching controller inter= rupts + * and completes them once the gadget responds via MMIO (the "USB devi= ce" + * section below). */ =20 #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/core/irq.h" #include "hw/core/registerfields.h" +#include "hw/core/qdev-properties.h" #include "hw/usb/aspeed-udc.h" #include "qemu/module.h" +#include "qapi/error.h" +#include "system/dma.h" +#include "system/address-spaces.h" #include "trace.h" =20 +#define AST_UDC_EP0_MAXPKT 64 + /* Root / Global registers (offset from the controller base) */ REG32(UDC_FUNC_CTRL, 0x00) FIELD(UDC_FUNC_CTRL, UPSTREAM_EN, 0, 1) @@ -90,11 +102,29 @@ REG32(EP_DMA_STS, 0x0C) FIELD(EP_DMA_STS, RPTR, 8, 8) FIELD(EP_DMA_STS, TX_SIZE, 16, 11) =20 +/* DMA descriptor ring (256-stage mode) and descriptor data limits */ +#define ASPEED_UDC_DESCS_COUNT 256 +#define ASPEED_UDC_DESC_MAX_LEN 4096 + +/* DMA processing-status idle codes (EP_DMA_CTRL PROC_STS field) */ +#define EP_DMA_CTRL_STS_RX_IDLE 0x0 +#define EP_DMA_CTRL_STS_TX_IDLE 0x8 + +/* DMA descriptor (DES1) fields, in guest memory */ +#define AST_EP_DESC1_IN_LEN(w1) ((w1) & 0x1fff) +/* interrupt-on-completion */ +#define AST_EP_DESC1_INTR BIT(31) + /* Device-reset default: root, DMA and EP-pool soft-reset bits set (0x301)= */ #define UDC_DEV_RESET_DEFAULT \ (R_UDC_DEV_RESET_ROOT_MASK | R_UDC_DEV_RESET_DMA_MASK | \ R_UDC_DEV_RESET_EP_POOL_MASK) =20 +static bool aspeed_udc_ep_serve_in(AspeedUDCState *s, unsigned ep, + USBPacket *p, bool *complete); +static bool aspeed_udc_ep_deliver_out(AspeedUDCState *s, unsigned ep, + USBPacket *p); + static void aspeed_udc_update_irq(AspeedUDCState *s) { bool level; @@ -107,6 +137,159 @@ static void aspeed_udc_update_irq(AspeedUDCState *s) qemu_set_irq(s->irq, level); } =20 +static void aspeed_udc_raise_isr(AspeedUDCState *s, uint32_t mask) +{ + s->regs[R_UDC_ISR] |=3D mask; + aspeed_udc_update_irq(s); +} + +/* Signal completion of a programmable-endpoint transaction to the gadget = */ +static void aspeed_udc_raise_ep_ack(AspeedUDCState *s, unsigned ep) +{ + trace_aspeed_udc_ep_ack(ep); + s->regs[R_UDC_EP_ACK_ISR] |=3D BIT(ep); + s->regs[R_UDC_ISR] |=3D R_UDC_ISR_EP_POOL_ACK_MASK; + aspeed_udc_update_irq(s); +} + +/* + * System bus device: MMIO register interface (guest gadget-driver facing) + */ + +/* Connect/disconnect the gadget from the host bus (UBD00 upstream enable)= */ +static void aspeed_udc_set_pullup(AspeedUDCState *s, bool on) +{ + USBDevice *udev; + Error *err =3D NULL; + + if (!s->usbgadget) { + /* no gadget device bound to this controller */ + return; + } + + udev =3D USB_DEVICE(s->usbgadget); + if (!udev->port) { + /* not attached to a host controller bus */ + return; + } + + trace_aspeed_udc_pullup(on, udev->attached); + if (on && !udev->attached) { + usb_device_attach(udev, &err); + if (err) { + warn_report_err(err); + } + } else if (!on && udev->attached) { + usb_device_detach(udev); + } +} + +/* Complete the in-flight EP0 control transfer back to the host */ +static void aspeed_udc_ep0_complete(AspeedUDCState *s, uint32_t len) +{ + USBPacket *p =3D s->ep0_packet; + + if (!p) { + return; + } + + s->ep0_packet =3D NULL; + p->actual_length =3D s->ep0_dir_in ? MIN(len, s->ep0_setup_len) + : s->ep0_setup_len; + p->status =3D USB_RET_SUCCESS; + trace_aspeed_udc_ep0_complete(s->ep0_dir_in, p->actual_length); + usb_generic_async_ctrl_complete(USB_DEVICE(s->usbgadget), p); +} + +/* + * The gadget driver drives EP0 by writing UBD30. Translate those writes i= nto + * data movement to/from the deferred host control packet plus the matching + * ACK interrupts the gadget expects. + */ +static void aspeed_udc_ep0_ctrl_write(AspeedUDCState *s, uint32_t val) +{ + uint32_t buf_addr =3D s->regs[R_UDC_EP0_DATA_BUFF]; + uint32_t store =3D val & R_UDC_EP0_CTRL_STALL_MASK; + uint32_t txlen; + USBPacket *p; + uint32_t n; + + trace_aspeed_udc_ep0_ctrl_write(val, s->ep0_dir_in, s->ep0_offset); + + /* Gadget stalled EP0: fail the pending control transfer */ + if (val & R_UDC_EP0_CTRL_STALL_MASK) { + p =3D s->ep0_packet; + + if (p) { + s->ep0_packet =3D NULL; + p->status =3D USB_RET_STALL; + usb_generic_async_ctrl_complete(USB_DEVICE(s->usbgadget), p); + } + s->regs[R_UDC_EP0_CTRL] =3D store; + return; + } + + if (val & R_UDC_EP0_CTRL_TX_RDY_MASK) { + txlen =3D FIELD_EX32(val, UDC_EP0_CTRL, TX_LEN); + + if (s->ep0_dir_in && s->ep0_packet) { + /* IN data stage: copy a chunk from the gadget's DMA buffer */ + n =3D MIN(txlen, s->ep0_setup_len - s->ep0_offset); + + if (n) { + dma_memory_read(&address_space_memory, buf_addr, + s->ep0_data + s->ep0_offset, n, + MEMTXATTRS_UNSPECIFIED); + } + s->ep0_offset +=3D n; + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_IN_ACK_MASK); + + if (txlen < AST_UDC_EP0_MAXPKT || + s->ep0_offset >=3D s->ep0_setup_len) { + aspeed_udc_ep0_complete(s, s->ep0_offset); + } + } else { + /* Zero-length status IN for an OUT / no-data control transfer= */ + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_IN_ACK_MASK); + aspeed_udc_ep0_complete(s, s->ep0_offset); + } + + } else if (val & R_UDC_EP0_CTRL_RX_RDY_MASK) { + if (s->ep0_dir_in) { + /* Status stage OUT (zero length) following IN data */ + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_OUT_ACK_MASK); + } else if (s->ep0_packet) { + /* OUT data stage: hand a chunk of host data to the gadget */ + n =3D MIN(s->ep0_setup_len - s->ep0_offset, + AST_UDC_EP0_MAXPKT); + + if (n) { + dma_memory_write(&address_space_memory, buf_addr, + s->ep0_data + s->ep0_offset, n, + MEMTXATTRS_UNSPECIFIED); + } + s->ep0_offset +=3D n; + store =3D FIELD_DP32(store, UDC_EP0_CTRL, RX_LEN, n); + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_OUT_ACK_MASK); + } + } + + s->regs[R_UDC_EP0_CTRL] =3D store; +} + +/* The upstream-enable bit connects/disconnects the gadget from the host b= us */ +static void aspeed_udc_func_ctrl_write(AspeedUDCState *s, uint32_t val) +{ + bool was_on =3D FIELD_EX32(s->regs[R_UDC_FUNC_CTRL], + UDC_FUNC_CTRL, UPSTREAM_EN); + bool now_on =3D FIELD_EX32(val, UDC_FUNC_CTRL, UPSTREAM_EN); + + s->regs[R_UDC_FUNC_CTRL] =3D val; + if (now_on !=3D was_on) { + aspeed_udc_set_pullup(s, now_on); + } +} + static uint64_t aspeed_udc_read(void *opaque, hwaddr offset, unsigned size) { AspeedUDCState *s =3D ASPEED_UDC(opaque); @@ -132,6 +315,12 @@ static void aspeed_udc_write(void *opaque, hwaddr offs= et, uint64_t data, /* Status registers are write-1-to-clear */ s->regs[reg] &=3D ~val; break; + case R_UDC_FUNC_CTRL: + aspeed_udc_func_ctrl_write(s, val); + break; + case R_UDC_EP0_CTRL: + aspeed_udc_ep0_ctrl_write(s, val); + return; default: s->regs[reg] =3D val; break; @@ -154,6 +343,70 @@ static const MemoryRegionOps aspeed_udc_ops =3D { }, }; =20 +/* + * Descriptor-mode IN kick: the gadget wrote EP_DMA_STS to advance its wri= te + * pointer (queue more IN data). Adopt the write pointer, keep our read po= inter + * (the DMA engine owns it), then serve and complete a parked IN poll if a= ny. + */ +static void aspeed_udc_ep_dma_kick(AspeedUDCState *s, unsigned ep, uint32_= t val) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t rptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, RPTR); + uint32_t rptr_w =3D FIELD_EX32(val, EP_DMA_STS, RPTR); + uint32_t wptr =3D FIELD_EX32(val, EP_DMA_STS, WPTR); + uint32_t sts =3D e->regs[R_EP_DMA_STS]; + USBPacket *p =3D e->pkt; + bool complete; + + sts =3D FIELD_DP32(sts, EP_DMA_STS, WPTR, wptr); + + /* + * A normal kick writes only the write pointer (bits[7:0]); the read-p= ointer + * field is zero and is ours to advance. Only an explicit non-zero read + * pointer equal to the write pointer (the driver draining the ring, e= .g. on + * reset) resets our read pointer. Testing rptr=3D=3Dwptr alone would = wrongly + * match a normal kick whose write pointer just wrapped to 0, resettin= g the + * ring and stranding a parked packet until the host times out. + */ + if (rptr_w !=3D 0 && rptr_w =3D=3D wptr) { + rptr =3D rptr_w; + e->desc_off =3D 0; + } + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(sts, EP_DMA_STS, RPTR, rptr); + + if (p && rptr !=3D wptr) { + if (aspeed_udc_ep_serve_in(s, ep, p, &complete)) { + aspeed_udc_raise_ep_ack(s, ep); + } + if (complete) { + e->pkt =3D NULL; + p->status =3D USB_RET_SUCCESS; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + } + } +} + +/* + * Single-stage OUT arm: the gadget wrote EP_DMA_STS to offer a receive + * buffer. If a host OUT packet is parked waiting for it, + * deliver into the buffer now and complete it (or keep it parked if the h= ost + * data phase is larger than this one armed chunk). + */ +static void aspeed_udc_ep_out_kick(AspeedUDCState *s, unsigned ep) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + USBPacket *p =3D e->pkt; + + if (!p || !FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, WPTR)) { + return; + } + if (aspeed_udc_ep_deliver_out(s, ep, p)) { + e->pkt =3D NULL; + p->status =3D USB_RET_SUCCESS; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + } +} + static uint64_t aspeed_udc_ep_read(void *opaque, hwaddr offset, unsigned s= ize) { AspeedUDCEP *e =3D opaque; @@ -167,9 +420,33 @@ static void aspeed_udc_ep_write(void *opaque, hwaddr o= ffset, uint64_t data, unsigned size) { AspeedUDCEP *e =3D opaque; - - trace_aspeed_udc_ep_write(e->index, offset, data); - e->regs[offset >> 2] =3D data; + AspeedUDCState *s =3D e->udc; + uint32_t reg =3D offset >> 2; + uint32_t val =3D data; + uint32_t ctrl; + + trace_aspeed_udc_ep_write(e->index, offset, val); + + /* + * A write to EP_DMA_STS is a DMA kick. In descriptor mode (IN) it adv= ances + * the ring and resumes a parked IN poll; in single-stage mode (OUT) it + * arms a receive buffer and delivers a parked OUT packet. Every other + * EP register is plain storage. + */ + if (reg =3D=3D R_EP_DMA_STS) { + ctrl =3D e->regs[R_EP_DMA_CTRL]; + + if (FIELD_EX32(ctrl, EP_DMA_CTRL, DESC_OP_EN)) { + /* descriptor-mode IN */ + aspeed_udc_ep_dma_kick(s, e->index, val); + } else { + /* single-stage OUT */ + e->regs[reg] =3D val; + aspeed_udc_ep_out_kick(s, e->index); + } + } else { + e->regs[reg] =3D val; + } } =20 static const MemoryRegionOps aspeed_udc_ep_ops =3D { @@ -206,6 +483,7 @@ static void aspeed_udc_realize(DeviceState *dev, Error = **errp) for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { g_autofree char *name =3D g_strdup_printf(TYPE_ASPEED_UDC ".ep%d",= i); =20 + s->ep[i].udc =3D s; s->ep[i].index =3D i; s->ep[i].regs =3D g_new0(uint32_t, ASPEED_UDC_EP_NR_REGS); memory_region_init_io(&s->ep[i].mr, OBJECT(s), &aspeed_udc_ep_ops, @@ -226,8 +504,26 @@ static void aspeed_udc_reset_hold(Object *obj, ResetTy= pe type) memset(s->regs, 0, ASPEED_UDC_NR_REGS * sizeof(uint32_t)); for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { memset(s->ep[i].regs, 0, ASPEED_UDC_EP_NR_REGS * sizeof(uint32_t)); + s->ep[i].pkt =3D NULL; + s->ep[i].desc_off =3D 0; } s->regs[R_UDC_DEV_RESET] =3D UDC_DEV_RESET_DEFAULT; + s->ep0_packet =3D NULL; + + /* + * A machine reset (e.g. a guest reboot) wipes the gadget driver, so a + * device still attached to the host bus from before the reset is now + * dead. Detach it, otherwise the rebooted host tries to re-enumerate a + * gadget with no driver behind it and fails with -110. It re-attaches + * when the new gadget asserts pull-up (UBD00 upstream-enable). + */ + if (s->usbgadget) { + USBDevice *udev =3D USB_DEVICE(s->usbgadget); + + if (udev->attached) { + usb_device_detach(udev); + } + } } =20 static void aspeed_udc_unrealize(DeviceState *dev) @@ -252,6 +548,370 @@ static void aspeed_udc_class_init(ObjectClass *klass,= const void *data) rc->phases.hold =3D aspeed_udc_reset_hold; } =20 +/* + * USB device: gadget presented on a host controller's bus + * + * These callbacks run in the context of the host controller. They transla= te + * host transactions into the controller interrupts/state the gadget driver + * expects, then defer (USB_RET_ASYNC) until the driver responds through t= he + * MMIO register interface above. + */ + +/* Locate the programmable endpoint matching a host transaction */ +static int aspeed_udc_find_ep(AspeedUDCState *s, int ep_nr, bool is_out) +{ + uint32_t cfg; + int i; + + for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { + cfg =3D s->ep[i].regs[R_EP_CONFIG]; + + if (!FIELD_EX32(cfg, EP_CONFIG, ENABLE) || + FIELD_EX32(cfg, EP_CONFIG, EP_NUM) !=3D ep_nr) { + continue; + } + if (!!FIELD_EX32(cfg, EP_CONFIG, DIR_OUT) =3D=3D is_out) { + return i; + } + } + return -1; +} + +/* + * IN endpoint, descriptor-list mode: serve the descriptors the gadget que= ued + * (read pointer .. write pointer) into the host IN packet. + * + * The host controller coalesces a multi-packet bulk IN into one packet wh= ose + * iovec may span several descriptors, so drain as many queued descriptors= as + * fit. A descriptor bigger than the remaining room is served partially and + * resumed on the next poll via desc_off; the read pointer only advances o= nce a + * descriptor is fully consumed (so the gadget driver, which reads the read + * pointer back, learns exactly what completed). + * + * *complete is set true when the host transaction is finished (packet ful= l, or + * a short/zero-length descriptor ended the gadget's transfer) and false w= hen + * the ring ran dry with the packet still short - the caller then keeps the + * packet parked and resumes on the next kick, rather than completing it s= hort + * (a maxpacket-multiple short read is not seen as end-of-transfer by the = host, + * which would then wait forever for data the gadget considers already sen= t). + * + * Returns true when the gadget should be signalled (EP ACK): the ring dra= ined + * (its descriptor handler waits for read=3D=3Dwrite) or a descriptor aske= d for an + * interrupt. + */ +static bool aspeed_udc_ep_serve_in(AspeedUDCState *s, unsigned ep, + USBPacket *p, bool *complete) +{ + QEMUIOVector *pktiov =3D p->combined ? &p->combined->iov : &p->iov; + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t mps =3D FIELD_EX32(e->regs[R_EP_CONFIG], EP_CONFIG, MAX_PKT); + uint32_t base =3D e->regs[R_EP_DMA_BUFF]; + uint32_t sts =3D e->regs[R_EP_DMA_STS]; + uint32_t wptr =3D FIELD_EX32(sts, EP_DMA_STS, WPTR); + uint32_t rptr =3D FIELD_EX32(sts, EP_DMA_STS, RPTR); + uint32_t remaining; + uint8_t buf[1024]; + /* des_0: buffer base, des_1: control/len */ + uint32_t desc[2]; + uint32_t copied; + uint32_t chunk; + uint32_t dlen; + uint32_t room; + uint32_t seg; + uint32_t off; + uint32_t w0; + uint32_t w1; + bool done =3D false; + bool ack =3D false; + + if (mps =3D=3D 0) { + /* a MAX_PKT field of 0 means 1024 bytes */ + mps =3D 1024; + } + + trace_aspeed_udc_ep_data_in(ep, rptr, wptr, pktiov->size); + + while (rptr !=3D wptr) { + copied =3D 0; + dma_memory_read(&address_space_memory, base + rptr * sizeof(desc), + desc, sizeof(desc), MEMTXATTRS_UNSPECIFIED); + w0 =3D le32_to_cpu(desc[0]); + w1 =3D le32_to_cpu(desc[1]); + dlen =3D AST_EP_DESC1_IN_LEN(w1); + off =3D e->desc_off; + remaining =3D dlen > off ? dlen - off : 0; + room =3D pktiov->size > (uint32_t)p->actual_length ? + pktiov->size - (uint32_t)p->actual_length : 0; + chunk =3D MIN(remaining, room); + + while (copied < chunk) { + seg =3D MIN(chunk - copied, sizeof(buf)); + dma_memory_read(&address_space_memory, w0 + off + copied, buf,= seg, + MEMTXATTRS_UNSPECIFIED); + usb_packet_copy(p, buf, seg); + copied +=3D seg; + } + e->desc_off =3D off + chunk; + + if (e->desc_off < dlen) { + /* Room exhausted mid-descriptor: the packet is full */ + done =3D true; + break; + } + + /* Descriptor fully served: advance the read pointer */ + rptr =3D (rptr + 1) % ASPEED_UDC_DESCS_COUNT; + e->desc_off =3D 0; + if (w1 & AST_EP_DESC1_INTR) { + ack =3D true; + } + /* short/zero-length stage ends the transfer */ + if (dlen < mps) { + done =3D true; + break; + } + if ((uint32_t)p->actual_length >=3D pktiov->size) { + done =3D true; + break; + } + } + + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(sts, EP_DMA_STS, RPTR, rptr); + e->regs[R_EP_DMA_CTRL] =3D FIELD_DP32(e->regs[R_EP_DMA_CTRL], EP_DMA_C= TRL, + PROC_STS, EP_DMA_CTRL_STS_TX_IDLE); + /* The gadget driver completes its request when the ring drains */ + if (rptr =3D=3D wptr) { + ack =3D true; + } + + *complete =3D done; + return ack; +} + +static void aspeed_udc_ep_data_in(AspeedUDCState *s, unsigned ep, USBPacke= t *p) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t sts =3D e->regs[R_EP_DMA_STS]; + uint32_t rptr =3D FIELD_EX32(sts, EP_DMA_STS, RPTR); + uint32_t wptr =3D FIELD_EX32(sts, EP_DMA_STS, WPTR); + bool complete; + + if (rptr =3D=3D wptr) { + /* + * Nothing queued yet. Park the packet as ASYNC rather than NAKing= : the + * gadget will kick shortly and the kick handler serves and comple= tes + * it, waking the host immediately. NAK would leave the host to re= -poll + * on its much slower async-schedule timer, collapsing bulk throug= hput. + */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + return; + } + + if (aspeed_udc_ep_serve_in(s, ep, p, &complete)) { + aspeed_udc_raise_ep_ack(s, ep); + } + if (complete) { + p->status =3D USB_RET_SUCCESS; + } else { + /* + * Ring ran dry before the request was satisfied: keep it parked a= nd + * resume from the kick that queues the next chunk. + */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + } +} + +/* + * OUT endpoint, single-stage mode: deliver host OUT data into the buffer = the + * gadget armed (EP_DMA_BUFF + EP_DMA_STS). The host controller may + * hand the whole OUT data phase as one packet larger than the armed chunk= , so + * copy up to TX_SIZE bytes starting where the previous call left off + * (p->actual_length); the caller keeps the packet parked across re-arms u= ntil + * it is fully drained. Returns true once the packet is fully delivered. + */ +static bool aspeed_udc_ep_deliver_out(AspeedUDCState *s, unsigned ep, + USBPacket *p) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t buf_addr =3D e->regs[R_EP_DMA_BUFF]; + uint32_t chunk =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, TX_SI= ZE); + uint32_t remaining =3D p->iov.size - (uint32_t)p->actual_length; + uint32_t len =3D MIN(remaining, chunk); + uint8_t buf[ASPEED_UDC_DESC_MAX_LEN]; + + if (buf_addr && len) { + len =3D MIN(len, sizeof(buf)); + usb_packet_copy(p, buf, len); + dma_memory_write(&address_space_memory, buf_addr, buf, len, + MEMTXATTRS_UNSPECIFIED); + } + + /* Report the received length; clearing WPTR disarms the receive buffe= r */ + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(0, EP_DMA_STS, TX_SIZE, len); + e->regs[R_EP_DMA_CTRL] =3D FIELD_DP32(e->regs[R_EP_DMA_CTRL], EP_DMA_C= TRL, + PROC_STS, EP_DMA_CTRL_STS_RX_IDLE); + aspeed_udc_raise_ep_ack(s, ep); + + return (uint32_t)p->actual_length >=3D p->iov.size; +} + +static void aspeed_udc_ep_data_out(AspeedUDCState *s, unsigned ep, USBPack= et *p) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t sts =3D e->regs[R_EP_DMA_STS]; + + trace_aspeed_udc_ep_data_out(ep, FIELD_EX32(sts, EP_DMA_STS, WPTR), + FIELD_EX32(sts, EP_DMA_STS, TX_SIZE), + p->iov.size); + if (!FIELD_EX32(sts, EP_DMA_STS, WPTR)) { + /* + * No receive buffer armed yet. Park the packet as ASYNC rather th= an + * NAKing: delivering into a stale buffer would lose the packet (e= .g. a + * mass-storage CBW), and NAK would fall back to the slow async re= -poll. + * aspeed_udc_ep_out_kick() delivers it once the gadget arms a buf= fer. + */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + return; + } + + if (aspeed_udc_ep_deliver_out(s, ep, p)) { + p->status =3D USB_RET_SUCCESS; + } else { + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + } +} + +static void aspeed_udc_gadget_handle_reset(USBDevice *udev) +{ + AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + + s->ep0_packet =3D NULL; + s->ep0_offset =3D 0; + /* EHCI is high speed; let the gadget read the link speed from UBD24 */ + s->regs[R_UDC_STS] =3D R_UDC_STS_HIGHSPEED_MASK; + trace_aspeed_udc_reset(s->regs[R_UDC_IER]); + aspeed_udc_raise_isr(s, R_UDC_ISR_BUS_RESET_MASK); +} + +static void aspeed_udc_gadget_handle_control(USBDevice *udev, USBPacket *p, + int request, int value, int inde= x, + int length, uint8_t *data) +{ + AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + uint8_t type =3D request >> 8; + uint8_t req =3D request & 0xff; + + /* + * Reconstruct the 8-byte SETUP packet into the SETUP data buffer where + * the gadget driver reads it from (controller offset 0x80). + */ + s->regs[R_UDC_SETUP0] =3D type | (req << 8) | ((value & 0xffff) << 16); + s->regs[R_UDC_SETUP1] =3D (index & 0xffff) | ((length & 0xffff) << 16); + + s->ep0_packet =3D p; + s->ep0_data =3D data; + s->ep0_setup_len =3D length; + s->ep0_offset =3D 0; + s->ep0_dir_in =3D (type & USB_DIR_IN); + + trace_aspeed_udc_ep0_setup(type, req, value, index, length, + s->ep0_dir_in, udev->addr); + + /* + * SET_ADDRESS: adopt the address and complete the (zero-length) status + * synchronously, exactly like QEMU's generic control handler. The gad= get + * is still notified so its own state machine advances, but the host + * transfer must not depend on that asynchronous round-trip. + */ + if (type =3D=3D 0 && req =3D=3D USB_REQ_SET_ADDRESS) { + udev->addr =3D value; + s->ep0_packet =3D NULL; + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_SETUP_MASK); + p->status =3D USB_RET_SUCCESS; + return; + } + + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_SETUP_MASK); + p->status =3D USB_RET_ASYNC; +} + +static void aspeed_udc_gadget_handle_data(USBDevice *udev, USBPacket *p) +{ + AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + bool is_out =3D (p->pid =3D=3D USB_TOKEN_OUT); + int ep =3D aspeed_udc_find_ep(s, p->ep->nr, is_out); + + trace_aspeed_udc_handle_data(p->ep->nr, is_out ? "OUT" : "IN", + p->iov.size, ep); + if (ep < 0) { + p->status =3D USB_RET_STALL; + return; + } + + if (is_out) { + aspeed_udc_ep_data_out(s, ep, p); + } else { + aspeed_udc_ep_data_in(s, ep, p); + } +} + +static void aspeed_udc_gadget_cancel_packet(USBDevice *udev, USBPacket *p) +{ + AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + int i; + + if (s->ep0_packet =3D=3D p) { + s->ep0_packet =3D NULL; + } + for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { + if (s->ep[i].pkt =3D=3D p) { + s->ep[i].pkt =3D NULL; + } + } +} + +static void aspeed_udc_gadget_realize(USBDevice *udev, Error **errp) +{ + AspeedUDCGadget *dev =3D ASPEED_UDC_GADGET(udev); + + if (!dev->udc) { + error_setg(errp, "aspeed-udc-gadget: 'udc' link is not set"); + return; + } + /* Bind this gadget to its controller */ + dev->udc->usbgadget =3D dev; + + /* Connection to the host is driven by the gadget via UBD00 upstream-e= n */ + udev->auto_attach =3D 0; + /* The ASPEED UDC is USB 2.0, so it only runs at High-Speed for now */ + udev->speed =3D USB_SPEED_HIGH; + udev->speedmask =3D USB_SPEED_MASK_HIGH; +} + +static const Property aspeed_udc_gadget_props[] =3D { + DEFINE_PROP_LINK("udc", AspeedUDCGadget, udc, TYPE_ASPEED_UDC, + AspeedUDCState *), +}; + +static void aspeed_udc_gadget_class_init(ObjectClass *klass, const void *d= ata) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + USBDeviceClass *uc =3D USB_DEVICE_CLASS(klass); + + dc->desc =3D "ASPEED UDC gadget device"; + uc->product_desc =3D "ASPEED UDC gadget"; + uc->realize =3D aspeed_udc_gadget_realize; + uc->handle_reset =3D aspeed_udc_gadget_handle_reset; + uc->handle_control =3D aspeed_udc_gadget_handle_control; + uc->handle_data =3D aspeed_udc_gadget_handle_data; + uc->cancel_packet =3D aspeed_udc_gadget_cancel_packet; + device_class_set_props(dc, aspeed_udc_gadget_props); +} + static const TypeInfo aspeed_udc_types[] =3D { { .name =3D TYPE_ASPEED_UDC, @@ -259,6 +919,12 @@ static const TypeInfo aspeed_udc_types[] =3D { .instance_size =3D sizeof(AspeedUDCState), .class_init =3D aspeed_udc_class_init, }, + { + .name =3D TYPE_ASPEED_UDC_GADGET, + .parent =3D TYPE_USB_DEVICE, + .instance_size =3D sizeof(AspeedUDCGadget), + .class_init =3D aspeed_udc_gadget_class_init, + }, }; =20 DEFINE_TYPES(aspeed_udc_types) diff --git a/hw/usb/trace-events b/hw/usb/trace-events index fb79e24fba..77958fe79a 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -383,4 +383,13 @@ aspeed_udc_read(uint64_t offset, uint64_t value) "offs= et 0x%" PRIx64 " value 0x% aspeed_udc_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " va= lue 0x%" PRIx64 aspeed_udc_ep_read(int ep, uint64_t offset, uint64_t value) "ep %d, offset= 0x%" PRIx64 " value 0x%" PRIx64 aspeed_udc_ep_write(int ep, uint64_t offset, uint64_t value) "ep %d, offse= t 0x%" PRIx64 " value 0x%" PRIx64 +aspeed_udc_pullup(int on, int attached) "on %d, attached %d" aspeed_udc_irq(uint32_t isr, uint32_t ier, int level) "isr 0x%x, ier 0x%x,= level %d" +aspeed_udc_reset(uint32_t ier) "bus reset, ier 0x%x" +aspeed_udc_ep0_setup(uint8_t type, uint8_t req, uint16_t value, uint16_t i= ndex, uint16_t length, int dir_in, int addr) "bmRequestType 0x%02x, bReques= t 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength %d, dir_in %d, addr %d" +aspeed_udc_ep0_ctrl_write(uint32_t val, int dir_in, uint32_t offset) "val = 0x%x, dir_in %d, off %u" +aspeed_udc_ep0_complete(int dir_in, int actual) "dir_in %d, actual %d" +aspeed_udc_handle_data(int ep_nr, const char *dir, uint32_t iov, int ep_id= x) "ep_nr %d, %s, iov %u, ep_idx %d" +aspeed_udc_ep_data_in(unsigned ep, uint32_t rptr, uint32_t wptr, uint32_t = iov) "ep %u, rptr %u, wptr %u, iov %u" +aspeed_udc_ep_data_out(unsigned ep, uint32_t wptr, uint32_t avail, uint32_= t iov) "ep %u, wptr %u, avail %u, iov %u" +aspeed_udc_ep_ack(unsigned ep) "ep %u" diff --git a/include/hw/usb/aspeed-udc.h b/include/hw/usb/aspeed-udc.h index eb279dd9c3..cbfd1e30fb 100644 --- a/include/hw/usb/aspeed-udc.h +++ b/include/hw/usb/aspeed-udc.h @@ -10,11 +10,19 @@ #define HW_USB_ASPEED_UDC_H =20 #include "hw/core/sysbus.h" +#include "hw/usb/usb.h" #include "qom/object.h" =20 #define TYPE_ASPEED_UDC "aspeed.udc" OBJECT_DECLARE_SIMPLE_TYPE(AspeedUDCState, ASPEED_UDC) =20 +/* + * The gadget side of the controller is presented to a USB host controller= 's + * bus as a single USB device that delegates back to the AspeedUDCState. + */ +#define TYPE_ASPEED_UDC_GADGET "aspeed.udc-gadget" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedUDCGadget, ASPEED_UDC_GADGET) + /* * EP0 (control) is served through the root registers (UDC_EP0_*), so only * the 4 programmable endpoints get their own register bank / ep[] entry. @@ -36,10 +44,20 @@ OBJECT_DECLARE_SIMPLE_TYPE(AspeedUDCState, ASPEED_UDC) =20 typedef struct AspeedUDCEP { MemoryRegion mr; + AspeedUDCState *udc; int index; uint32_t *regs; + /* host packet parked until the gadget queues (IN) or arms (OUT) data = */ + USBPacket *pkt; + /* bytes of the current IN descriptor already served */ + uint32_t desc_off; } AspeedUDCEP; =20 +struct AspeedUDCGadget { + USBDevice parent_obj; + AspeedUDCState *udc; +}; + struct AspeedUDCState { SysBusDevice parent_obj; =20 @@ -49,6 +67,18 @@ struct AspeedUDCState { qemu_irq irq; uint32_t *regs; AspeedUDCEP ep[ASPEED_UDC_NUM_EP]; + /* gadget USB device bound to this controller (set at its realize) */ + AspeedUDCGadget *usbgadget; + + /* + * In-flight EP0 control transfer (host side), deferred until the gadg= et + * driver responds via MMIO. + */ + USBPacket *ep0_packet; + uint8_t *ep0_data; + uint32_t ep0_setup_len; + uint32_t ep0_offset; + bool ep0_dir_in; }; =20 #endif /* HW_USB_ASPEED_UDC_H */ --=20 2.53.0 From nobody Sun Jul 26 11:05:45 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass(p=quarantine dis=none) header.from=aspeedtech.com ARC-Seal: i=2; a=rsa-sha256; t=1783064680; cv=pass; d=zohomail.com; s=zohoarc; b=mFlp9uTHppn4fpp3gf5s+/s0e8W4Zr2Q1649lIAQnw5jUeua7/Gb+yrhv7uH098wUXsUlpHiVzClXafuj+VU6cOJE7OJO8/mASARMv02g9Se1j6dhShhAZ/DF97VN1Cqoh2/F8iS0wbDRHcfav1XnO5t1CBdvjE52bV4pAE9/EM= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783064680; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=5+y91dGg+hWNgOQxOevXvocpd565pzFhnAkFP/pt9o4=; b=lJxaIb8ODlxTDeVsVecZbKVICm+s2HsugpnvHAXBi6KHA9s2txGQvoVNJcLgGPYcBErUSUbP0demoIyLhedNmw73QIPEzEC/D3iTAwmC/Re/D882FYq0b2SO2EBQaEBO0OWvSnKi3dh3EO/Crt+0ugXuBefjFfaqjw+Dz66X3tk= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=aspeedtech.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783064679497942.0357517132583; Fri, 3 Jul 2026 00:44:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYZ4-0008Sv-C8; Fri, 03 Jul 2026 03:43:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYZ2-0008ST-NN; Fri, 03 Jul 2026 03:43:56 -0400 Received: from mail-koreacentralazlp170130006.outbound.protection.outlook.com ([2a01:111:f403:c40f::6] helo=SEYPR02CU001.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYZ1-0004IZ-8R; Fri, 03 Jul 2026 03:43:56 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by SEYPR06MB5040.apcprd06.prod.outlook.com (2603:1096:101:54::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.181.10; Fri, 3 Jul 2026 07:43:37 +0000 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3]) by TYZPR06MB4980.apcprd06.prod.outlook.com ([fe80::ea8a:7cb7:4822:2fb3%6]) with mapi id 15.21.0181.010; Fri, 3 Jul 2026 07:43:37 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Fly6HFd7hQTw/1L04wSunyk9+c3h85wl2+A3bsm1EJ7tyvaFJ8pwEmVp2Rgd3wkIO7zbO9GErykqT6PM/TApZ7G8pLhrSWDFjDazSXT6sH/2NP58Rv9HjOJy05+li0OcB+oQlbmZ55/uGWgdkewvxDhmSAxYr5WFvw7gXRjkRktdo8JptWjUj4eOVCWx1IhXow0mwen0BbOGWi/ItxFyV0AjEu6tfZiRDgNljUeSfKEtQHDT3a0JJErA5pl30jhs2tPA3EFi808IOjkn9O2Qysyn7Y2EOj+GxRcPEbZpxPDfrdO4p165zNCgSW4gW7EqmfZ096KunB3qddTymv0LIA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=5+y91dGg+hWNgOQxOevXvocpd565pzFhnAkFP/pt9o4=; b=GmCxoP+Li/Cf2KZkr2vQlheaGgsl9y7K6PYFt+VKIkAmJWWwgMotK2c1kR7ffl+sa5IKE/CM4u/ViklZhZl00YSWNz6QTkWw6LahvEgFJQzumj/SFcck0vTeBCyjhOAnk/ft1c6f292HnvwOOG4fUfoQ7F5d1ejTd0RndSnLycjf1FzOR1upa0N8t126RvCTOUqrR32X2oPk2uGPtCYIQSP6sucZd3w0KZu4w7dHrkcmJsNcDalcEzuARKl5ZYuvYn6jjVetPzpNfQ9mTv6pnW8xvH/K32SNuty6V4Je3riZI0BFgtpy+1RNZ4ZRzt9/t2aoevv3TPVTy+CWtNkPuA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=aspeedtech.com; dmarc=pass action=none header.from=aspeedtech.com; dkim=pass header.d=aspeedtech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aspeedtech.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5+y91dGg+hWNgOQxOevXvocpd565pzFhnAkFP/pt9o4=; b=JfOswZcbbECavYe2ANmlGGcbQU1gTRO73IXlQOKu8tuNDtlc8zVWs5YLiU5tjy/BMeUh19v0+W473TuRf0SevYCNmqRFLvzlk5tdpLBfS+WHdxbReep2obXEfVxWCTaQ6513aB5V63bb5fizhOYeh0x6A0bFfk8SAPIl8Rt+3lvYlK9+dD5sLm0IDjtbrOPeFhGRgvc3lXDZa8m14l4uKc4Rf91waRMJGoRvT70AtTB8erNjuOiei55qchJt00uzXMqD/RhDxMDptOE5dDVO3uj5vKvPjBLyWkpqP/fT28kmF6h+XXhEJGPcD/hBEityS1v6KJg5kCZgNeohLHhXEg== From: Jamin Lin To: Paolo Bonzini , Peter Maydell , =?iso-8859-1?Q?C=E9dric_Le_Goater?= , Steven Lee , Troy Lee , Kane Chen , Andrew Jeffery , Joel Stanley , "open list:ARM TCG CPUs" , "open list:All patches CC here" CC: Jamin Lin , Troy Lee Subject: [PATCH v1 3/3] hw/arm/aspeed_ast2600: Wire up the UDC Thread-Topic: [PATCH v1 3/3] hw/arm/aspeed_ast2600: Wire up the UDC Thread-Index: AQHdCr+oNl8HJH0GCUqipWD50Ge91w== Date: Fri, 3 Jul 2026 07:43:37 +0000 Message-ID: <20260703074332.1049473-4-jamin_lin@aspeedtech.com> References: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260703074332.1049473-1-jamin_lin@aspeedtech.com> Accept-Language: zh-TW, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=aspeedtech.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: TYZPR06MB4980:EE_|SEYPR06MB5040:EE_ x-ms-office365-filtering-correlation-id: 3b93e814-f44a-4f75-6b53-08ded8d6cafc x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|23010399003|376014|366016|1800799024|921020|38070700021|18002099003|22082099003|56012099006; x-microsoft-antispam-message-info: uaK8N+GSFjjQUx8W5ffmtU9AM5Kr7Twi/kAcHhjgH9il6gs6hmyIGnHb5etuE7uKIJ0zaPcbx8vtO017fFaLSKWIRJUwR5A0Se4oFfENiqb2DKvn+HApI28A5WS52fJddLqMc8FeLbFWNKXx29lKpLYemkZO7JdeJdbYqQdUF06OtGAehzkQgeNVM/51bNp3F87svHjfbblZLuWtUTEbcDdgBf0LZVdEH8AgKVQCL24UGTboxPRGsw8ye8+b8r+v/QQMvYu6kGOjiuWk2lFW8V64nEmUjC9Gz10NdE3B80H83CSedmFWS0lkkG5LMSyLBj37SjqJ6Hcpt8+5WX3rf7+mPuzkEqXrVWPFn3SUjd29SMardR28WYcQeDeAli7JjCZudkci96XXUKPPc8iWBMOxxYQPWEIpIq2Lx4+QVx+wyU4z0Pf/yRzOPdR8bHWCNx6tAdKHXkkNeiiOqfAEkdrll9KRO3v14mO63ZZFO/m1uDqLavC8KUS9ripsNcwRpL381U5qEItm9V1hYxmcQ5Me514sffmIlUlB5Jgi/yfsdXOR9gKFDeOxbm4E5plyHLaP4/TrfOscGZPWwlnJKlO6s1PGJfLiGszgj6nhhILRQhM0DaG07R27faCl1sjqQWZwwoR/8Gp64u8fZjGC39HucUR9+PN7ZTRv6VX/4S9Pri2pFQC5e/da6d3Qc5gaTz4LJB6bickEd0csq9IP/q8v9hRAuXN9GMdkLiD6M9k= x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:TYZPR06MB4980.apcprd06.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(23010399003)(376014)(366016)(1800799024)(921020)(38070700021)(18002099003)(22082099003)(56012099006); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?KBwSuM05G6P0RVayhN1FYyBfvAatD7/SzpVxkf9hJfZvd1hdMLYZfmE3pM?= =?iso-8859-1?Q?9Y7PZAS3DbB23QV1/u2X4NlvDAzBxa8ZjR3yNuWKfbWjdenr6gEcBA+eWb?= =?iso-8859-1?Q?vJZAG9qF7xcvbk/bQdCXalDgwkPMLSP7f4qL5MgZV0tLOR6/zq0ZopT4Dz?= =?iso-8859-1?Q?Bkrxv3SB+4p9vsS3uPcqPoGnVz7+R2hHdQIMECo/1gvhLeqkS707pulp4T?= =?iso-8859-1?Q?MTARuYdMwZcwzZSG6LiWi//rNYiXRw+mjkYYsGg6fI9NlsaHsazlMZoJBA?= =?iso-8859-1?Q?Uv9oifY2ZHYrYLNQZEREfyMrUQLIHmg2T3HyUSuUZQhK0Qig3RdLysyJ5o?= =?iso-8859-1?Q?q51JklWxLEkQ2wd5bXzkTdy68AHID7GWPsmdyDs8ayuLvIyLsJgD5e/NSm?= =?iso-8859-1?Q?DynuCNzQQHyF/dvOZxJxCay98xfNW5fHfp4KtwvjI9lmWzI2Wl9z0V7lZ0?= =?iso-8859-1?Q?b/XrRgUqkCI4papcCt8VNEq+P3M3Oq6tk6fHvaPVEufvqdHtpoZmVXyS0O?= =?iso-8859-1?Q?7nRFBDpFoXgmXSAcGtZltyIscl4z6FjIfv9unCMENanhS9FzBLq+elY0+7?= =?iso-8859-1?Q?BXo0lEuVPZRwd4xPYY3NUiRvEcjQ9S6rZzDu6adA8smOUpLhHMtEkpRyCr?= =?iso-8859-1?Q?QhctRBAYXqfdUPmXFG0Q3OwSpJn4XuBf+uVd0s6b8m4NIWuUu+IML5IgIS?= =?iso-8859-1?Q?WHxwKLh2vpek80a8vcKDMlHcEGocqZ/OVHjMt2m5C5ck3nh0TslM71VGuC?= =?iso-8859-1?Q?OZeXHIdUIUWN2WnBsHZq2pc8lK/+D5QP4OnYxraIoUVf97139rqf9KhdAG?= =?iso-8859-1?Q?Shbm6lAUDYNzijqHRTSJpHhBglmouM6by5m5gZxvV+fdI4XiioOnZRTPjn?= =?iso-8859-1?Q?WCMbzlEsMyNWOJvld8JlY7pcugZYkoV58Ty5AboNrOQv2qiAX1fLRQ/TRy?= =?iso-8859-1?Q?YDQ04fCP1dd8EmDoFrPyFhxW7zDoKAnQdI5N9k4VFzo4to+9xksjUi22VZ?= =?iso-8859-1?Q?wa3/dGmz837g5Hm4qvJaMbmvtVa6HDI338N36tnX0Dc/iMm4JR9C9109IE?= =?iso-8859-1?Q?W3MNbZrkoMswZE+fTJBpqSPrlsbtUGd1AWS3Nn1WBPiPPdoh0V1EcK7zpM?= =?iso-8859-1?Q?+Xk+N/ogsBe85cCS/CJ1Q2rv/mjeRkcu1Zz5G8bLyrYIhG72pEJXNpGlj4?= =?iso-8859-1?Q?zfpJEM5gNJvOwz6hu9IVxX6koa9RtQ6jRRQ1Zt/9y1W8CRilemgszWBQ3z?= =?iso-8859-1?Q?r6WEA13+dX/618bQD19eo2y4zI8/QiEwMdqhFqo76T0B5U4mMIAomCcQ+N?= =?iso-8859-1?Q?oP6RO930olGbo0yxsWYLJRc7yaXBNxEV+p5SeA0wnP5daYGrpEY94HD+TL?= =?iso-8859-1?Q?G6qAnmxitse+wqSSTKm6fte64yhKYqGvTPrTphVbZ2y12lSbLZvTUF2qeU?= =?iso-8859-1?Q?mwqFyeSwzjAVIUjq+sqY6KaEb1r4iEYaTeyIkrbCwNjVhYCP6rXG7Y/JZ8?= =?iso-8859-1?Q?A+iDkPAw9J0xERgUfnNHFRouK+rO+0Oaz3sZpDeT1MGfyJGGBantZG4cWO?= =?iso-8859-1?Q?rNPgCDE44GSHAqDIBCMa+SjwgI7n63rcwZ7c2GrlhCnQVpU6mA0mpRysJQ?= =?iso-8859-1?Q?+twPAvajE4U8NnHp5YtVQYo+I4iKQ3dMalngfONQKBuZ0SATQnyj9Frqts?= =?iso-8859-1?Q?egOo2CWqDBV4pbLf8TrrKfd+GMo7sbEXptefcfxliWbVpkM3A3ZuHHlHbT?= =?iso-8859-1?Q?cywAXEfxR3hvsy3DFTwoaUmezQsG+KbO7krNqVJBBm9RnXJetXchzXs7f7?= =?iso-8859-1?Q?4YadSwH3Ag=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: CElPP6vvMh4FLRHBu5Np0TS4kOyZ4gAxhtphcJIOhbY2sb11WxutL1tihQJ+kIa+AwxkzGwXebGSiFclutklzQw1myL6dqFFw8L3SPE2JbEH3ZfVVLm37933zHrd9g6kO/jhZYHnLzzlGgih15G1IWdte0VwEsUdlp0dRMPRwtfwBRuf1DuuN4IY98qm0nwxcxaRUTRyfMlY0M6x+GmQsnnxW2tQzvBQHXJ66AD8veCa2+v3mAAkEHqhUqyx+aktDf4k2dTp6SgmoT8HYT3RA5vqCKpAWpEdXL50OQnFKnOiZIecf7tpgDTRrP7M9QLau3PXmuNr1BseVdMx6essOQ== X-OriginatorOrg: aspeedtech.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: TYZPR06MB4980.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3b93e814-f44a-4f75-6b53-08ded8d6cafc X-MS-Exchange-CrossTenant-originalarrivaltime: 03 Jul 2026 07:43:37.2891 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 43d4aa98-e35b-4575-8939-080e90d5a249 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 2LCi99BEKcRgk84t+bbLOYDCg0rCLzDA2645DHKF3/2B8+ywUhzbnMJsdfvWzOVb/fr0uWjX3SIbOMGHjqKZNMY3l5RRn3MsbVpFxwUGkF8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SEYPR06MB5040 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a01:111:f403:c40f::6; envelope-from=jamin_lin@aspeedtech.com; helo=SEYPR02CU001.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @aspeedtech.com) X-ZM-MESSAGEID: 1783064681617158500 Content-Type: text/plain; charset="utf-8" Create the USB Device Controller (UDC) at 0x1e6a2000 on the AST2600 SoC: map its registers and connect its interrupt. The gadget USB device is not created by the SoC. It is a separate, user-creatable "aspeed.udc-gadget" USB device that the user plugs onto a USB host controller's bus; it finds its controller through the "udc" link property, e.g. -device aspeed.udc-gadget,udc=3D/machine/soc/udc Signed-off-by: Jamin Lin --- hw/arm/aspeed_ast2600.c | 13 +++++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index d1f18e471a..7d97f10448 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -32,6 +32,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] =3D { [ASPEED_DEV_SPI1] =3D 0x1E630000, [ASPEED_DEV_SPI2] =3D 0x1E631000, [ASPEED_DEV_EHCI1] =3D 0x1E6A1000, + [ASPEED_DEV_UDC] =3D 0x1E6A2000, [ASPEED_DEV_EHCI2] =3D 0x1E6A3000, [ASPEED_DEV_MII1] =3D 0x1E650000, [ASPEED_DEV_MII2] =3D 0x1E650008, @@ -113,6 +114,7 @@ static const int aspeed_soc_ast2600_irqmap[] =3D { [ASPEED_DEV_SDHCI] =3D 43, [ASPEED_DEV_EHCI1] =3D 5, [ASPEED_DEV_EHCI2] =3D 9, + [ASPEED_DEV_UDC] =3D 9, [ASPEED_DEV_EMMC] =3D 15, [ASPEED_DEV_GPIO] =3D 40, [ASPEED_DEV_GPIO_1_8V] =3D 11, @@ -214,6 +216,8 @@ static void aspeed_soc_ast2600_init(Object *obj) TYPE_PLATFORM_EHCI); } =20 + object_initialize_child(obj, "udc", &a->udc, TYPE_ASPEED_UDC); + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), @@ -573,6 +577,15 @@ static void aspeed_soc_ast2600_realize(DeviceState *de= v, Error **errp) ASPEED_DEV_EHCI1 + i= )); } =20 + /* UDC - USB 2.0 Device Controller */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->udc), errp)) { + return; + } + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->udc), 0, + sc->memmap[ASPEED_DEV_UDC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->udc), 0, + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_UDC)); + /* SDMC - SDRAM Memory Controller */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 3aac144cd4..74fd83e957 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -36,6 +36,7 @@ #include "hw/gpio/aspeed_sgpio.h" #include "hw/sd/aspeed_sdhci.h" #include "hw/usb/hcd-ehci.h" +#include "hw/usb/aspeed-udc.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" #include "hw/misc/unimp.h" @@ -138,6 +139,7 @@ struct Aspeed2600SoCState { =20 A15MPPrivState a7mpcore; ARMCPU cpu[ASPEED_CPUS_NUM]; /* XXX belong to a7mpcore */ + AspeedUDCState udc; }; =20 #define TYPE_ASPEED2600_SOC "aspeed2600-soc" --=20 2.53.0