From nobody Sat Sep 26 22:14: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=1788315445; cv=pass; d=zohomail.com; s=zohoarc; b=jhqF+WKh+qvxMvd4pXUeQNlYgftVw5rCgVZkqmyGCEdG4pFomtjXrcxzmTVNs6FlvIG1a1L3LHKpELvBN17AJti1UzIja0YxqwZBybEvqC9D/W/uIfosjM3lE95pIpF5bOqBIrtSMn5YW8MCUmKlWXs5qB0qdhG1q0tbXLL/cug= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788315445; 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=rjXgjVpdN+FUzSjdjIokViJKjfvXfGOX6gSsoEPF9wY=; b=Blma1JSI63Qyq/vZKD1M15Z1GDS5HJiKQwoOp4QbzT626QF1+Nvuytqyo9OJuBthCbt1NVr2la3+kRae7mkq3/1pzq88JUwHbF55qDU9D5TVhspqQmK5eDDU6oR7eiBCuTXESU5Rbut5Y+sBBI6TlpOqlsyRzRcvBcdJweFHkpE= 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 1788315444706969.7935127811492; Tue, 1 Sep 2026 19:17:24 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1aWH-0003Ny-Cl; Tue, 01 Sep 2026 22:16:09 -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 1x1aWF-0003Mb-3z; Tue, 01 Sep 2026 22:16:07 -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 1x1aWC-0007bI-Oq; Tue, 01 Sep 2026 22:16:06 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by TYZPR06MB5782.apcprd06.prod.outlook.com (2603:1096:400:26a::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Wed, 2 Sep 2026 02:15:44 +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.0360.008; Wed, 2 Sep 2026 02:15:44 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=MFXMm7LZE+LCMIWXzDHF0psrWlOuqZJ2koIxWXIo/9mFVwoMytzJfGXJ777vuNssyoM3XKMcXcyAK4uKCwL8gLxVdTuEZS1aWunA2ixbNo5aVmQgbwN9DJVZARp492UXsRlKsR5S/S/a3/HtRzS5sHLmKgLgLYJlnWU5+uQFtd8mWegOomC016ls+VBrdThVEJpywQDVwsnbZF5bbsgkOythvnq9DTfMcVxx4de825IATCOdzgwiFnTeha2uwHRv8diJBxHuEAGx/U+UQuhf2IdyvE5tYJxMbQPte3clT/RUDmQzwcbogUoooDxBNfI4sKxWOruRAAt1iokkzlFfpQ== 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=rjXgjVpdN+FUzSjdjIokViJKjfvXfGOX6gSsoEPF9wY=; b=X6PcQcJ74306H3VG6Hpg71cnIhMn9QHl8o6aLL6JANLTqvipRfwo85TMr9xlT1ubJue4f5+lViGGiSIZDn7GCYt6zIkPWAWVgUWgtKTAMFhJzv4jR9BGwC1ycMGh6aE4bcHczC3srVnCVWLt224bqEeRhh83ZSIB8U5XRoDY+usicQvhJpygCjjceWV4fY/Xc4THx89o4cW1NKsNR7OYJfnU0Rez7ce7Vw1zPmksl88PtlrNNZdFX8xVk74nSC8MeNtFwQNV3aRX8K9P4TBsNwpIR4YT+d5a/y0X9ZeSFbIagvb6pZAvn0q0gsxq9aZLUAwq+9C6u7qSWzRViVv8gA== 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=rjXgjVpdN+FUzSjdjIokViJKjfvXfGOX6gSsoEPF9wY=; b=Vc8FcD0UGRTb/QFjhNHN6lN5hnVhTAOZ60y+CelzETbibxBUjkYp97YuIwuVbwhbp3abPq27obTIXGRQ2FXifUFVaRoEfOUSqa2LiCg1QKOoNRB6rrEe10iBP4TU0EZx9d8cdMQQbiqgOTwCF3YRpyFdhtcJDhvS6vj4KP4mx35xIBIn8LEvFUhSD7oYCqmCcqV8U0CD4ULxLB90PLJ8X5jTCgBcKTJ3AyodNNIXPEl+q7csluIETqUScG8xc4r4QtMtA55BHp81K1EEwm9S7+qnDjq2Sq8ygf9xZ7M26+DE2kzdF4m6S6+AahU+qBm8/HSL66/ylIAED99JqUTk5A== 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 , =?iso-8859-1?Q?Philippe_Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric_Le_Goater?= Subject: [PATCH v5 1/4] hw/usb/aspeed-udc: Add ASPEED UDC device controller Thread-Topic: [PATCH v5 1/4] hw/usb/aspeed-udc: Add ASPEED UDC device controller Thread-Index: AQHdOoD1nKf0Ktqey02zyYXzNrzEIw== Date: Wed, 2 Sep 2026 02:15:44 +0000 Message-ID: <20260902021542.3194812-2-jamin_lin@aspeedtech.com> References: <20260902021542.3194812-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260902021542.3194812-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_|TYZPR06MB5782:EE_ x-ms-office365-filtering-correlation-id: e39bb7e2-18e2-4f2a-b2ba-08df0898186b x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|1800799024|376014|7416014|366016|23010399003|3023799007|10067099003|56012099006|921020|6133799003|38070700021|18002099003|22082099003; x-microsoft-antispam-message-info: /R9grVezfwJIWAh6jdLNr6Y0WAORIBmd2DbOkd3orzL7j/n/FmJdZj+H5WjuGFhXpQPA3VlXGY2ahOVWEIJMrVCXxA+MOsP0A3qEHCLLOgrZ3ZqmvFTNGl0fGRG5wYPbBJiCtaFI9JPB0mjr9GyDspv/kL79q8YW0NnUoUDGKDR3jRXsuKtY0G5a27XGfoBLEu8IjazANVl0XnKCQr2kc0aEVw+LqkYYVdq06rU3c5FRcMkRnIMUGUGeitd3J407pVKeXeScW3v0ArmMuRj/UqYKCaPpAyYAgEMFP50sH1P21QZaIBqQ7m+h1dr5opXOxG7aPd0rI4jKriQqr8MBDpyI+24ZcgLNDrrAoTQFpRiS2jW+eeCrm05PFITuI3yTd+E6n1Xi4QsCZnBHKuiVssB8Ff0PWDKDe8GONfNqoHxGD7gcFqu94GGPSCBeUxEsXO4bzEZA8ubFtOhvdLLVyfARr/lxujdo5PqoVklYGYWQ2il1hKAeEqGPsRdgdo2M6Tsjr1HOdZ6MulY34HUWv8aAN7EHkRvj0mZfYTyo0zQKgHgH9xPJoRZP63b1bIL7aHGgcfpkE7TsYsbkHTWu5CNxkDpvjtcO+KmcTsbcW6PuLV465VIpaZ05RTz6G5PqkdmwQSD+2W5sAyj4IPyitEklexUN6wp6JUVgDdXLTnz6k/w9W1LNoGaU654F5BWXSG2YeEk2Zfi1FGrXJZDE8KItHejp32GawmqJreLJ4gz/hiqGE3PPjnWVhy9k9e4F 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)(1800799024)(376014)(7416014)(366016)(23010399003)(3023799007)(10067099003)(56012099006)(921020)(6133799003)(38070700021)(18002099003)(22082099003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?jQ/WIE6BJX25qXcbArMygJ+hoXahXuB0UfdGrwaM136TY83eGbnGbBKVs9?= =?iso-8859-1?Q?IT7LsOFtweqWEAliwEKxqGis6oOjmaJAflmP3TZ+V00GbJBGJe0i9RNGCk?= =?iso-8859-1?Q?w4M07whI7X42V6edhuJNbYEapLRcZFKwn7AXIjSHOUmha2eNvJXW+O5JQp?= =?iso-8859-1?Q?quhBrKJHX0BR2D2vPa7qpo6GkmQpafF/IHIM1CckfuuI2bJfz4zpZy4EKM?= =?iso-8859-1?Q?7k6QkCC5nYZNTHPP/FZ6KFbkWXGTIynMCbDA+SvO2zmwZG4xucthYPea5q?= =?iso-8859-1?Q?dJAYlJP53IyuT689LKeKeXe0SriqKQX+0g586CLxSFwII1qOdmpf4hlt4a?= =?iso-8859-1?Q?8u3fchx0O8FSKE1NOJ2OXnfeo+FRsiX92tU3nnXs7IkNqfxK9a0SYUbQUQ?= =?iso-8859-1?Q?l3YoM1086JhNII+95Y7TsPdTEQ4DGwfDaKWc9qWUr4ocepF6cSM1qnbq0V?= =?iso-8859-1?Q?2j1RIKClizAyV3jg3MLgt4l6LwGryFLQFI4Jujjm/r+awdlMZuAVZOjOgi?= =?iso-8859-1?Q?595FYy9/oPcgTSHG3+ymJAWLD4a34EOOw9irDaBQAXafly1iZb9zmtJ7XA?= =?iso-8859-1?Q?qrFJ1Fjr6+ge2GL4Tl61fINa1LiMCT4RlT2mYdL37v7COiYWHzlhv1U5Uk?= =?iso-8859-1?Q?xJCO8o7tPUezR+VIFiZZuxKZy5DoXG6J75b/CHug86xgLLscxKtKJPkOsR?= =?iso-8859-1?Q?0UBAqDweXXIBrHMbzxwRMrpvGPE3azhOi1V/s/EYL/uHcFgnjsefjXTsz2?= =?iso-8859-1?Q?KSiAPWiuRuMWmJTPwAnoAEZyquNSrhZa0cOkoTgjj9//AsfqiO2Gbx/kVb?= =?iso-8859-1?Q?mgMQvv9MsNYa7M7t3sFDcjOMEaLcNXVqNK9/MH+ms1ulUEMfjIn0+332XX?= =?iso-8859-1?Q?nA0Bo4okFwkkIdlAdWGXCbl4KpNySxOtv3LJQ+0fL4OfmcUdJCLrhUqU/Y?= =?iso-8859-1?Q?IZFneB7CAWMNeRFPl0MndcPxEbsqhmtWQcRR05F0UNwJrLs7BPUfZzcrXj?= =?iso-8859-1?Q?34Lyvg1Y30MFnvPwzbGBC/xD2oeHhZ7ETLUDzyH0LW1ZT59qBM5tagHyzB?= =?iso-8859-1?Q?7rB8UUHoohA3iouRJ4PkyKonD1D9nlU523oeL3K0j+kz+z8h5yk1LJIqN3?= =?iso-8859-1?Q?YP5fN+E6l+ZZczIEXOKkRVsMlVrqKLkTwMFefjHeyLoPM704McPYqiYXny?= =?iso-8859-1?Q?gru+KAnHp2NCFVNifJsgOHhb4hZGGOmaQuhKNSgb1Rurmon+HgUOFd6/WX?= =?iso-8859-1?Q?EDOoE3JEAsKJc3kn/zL0wy5AkyKK2dZL9IopwQC6134J/lTAuijMsWWtMX?= =?iso-8859-1?Q?osbe1I95fSl/+2ETvz+lwEhizLw7XUsKpGRM3GmGtS18N0l1oR/7NshALY?= =?iso-8859-1?Q?brvs+OrRiwSx4pA8AU9Q74br1EoAzjYqAMH+PZKq/xE/S7ksjoMA8K9oNl?= =?iso-8859-1?Q?0eyblB69QIdjBRWarYvQ5SGIaB99Vkm3TTynyqgEZkFgJjZs1PFJ7ivrrW?= =?iso-8859-1?Q?rGlBvtZG9tibFNwg8nEnhMm6ip2z/1uLweptcyXJptL/Byr/RnxuvUU/KR?= =?iso-8859-1?Q?7AL/h8AgahcEfj3D4FjlzCH76VqxkTyQmYrbJ6Da0ihoY/SeXSxT/l1kGo?= =?iso-8859-1?Q?jt8K0mKRgEIUrrBdfXE06p3ArbVqriW0AindpolfF/dfzG/ic+wPkrXaPW?= =?iso-8859-1?Q?DGMsnBmx4H/Psh77d/ica5bR+hJErfskO9Wnhise5ejDJyE0OzSac3ABd9?= =?iso-8859-1?Q?doFow7DFXYJJJntgbmNK75rHO/1WsTUz+rNnNiZ6DIz8u4hzitsmRvQWms?= =?iso-8859-1?Q?1GiH6TO2vw=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: z85DHMeFtoJ1FvXBeGlPPsT4hQO7nrFjiZCi7aAXV5Oa+1LC9TQnstRV7SRIvCq2ltyj6X4m+xdGOsetJeXaRFBt7OPMLBQVl6ZD42CT5qttgmY/LEv6YUAVvHXx3Iy2AQPpknMZU15kZxxZKNnstAgkpzlXULZPyIXZr1GRuIo9HCjc8ddz0V/vYCsI94fEpTA6ccNIuFl3OxnZJdw2oSpKH0aGuV5ya2W2RXcpum2/H+fSrRlamAxMVj5OI6OsAYK2GRZk1kgzpHQdiijVAWn5qmCOsebDD6P/boTctk912zgrUu40XXyCTBFhtGsymd9+4Gv3JbU0bgzvQH1TUw== 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: e39bb7e2-18e2-4f2a-b2ba-08df0898186b X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Sep 2026 02:15:44.1869 (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: c6rZ1rryzEEFEVMxF8xNbUMhuLoI3bZnZSWDxqW+Nbf0fGQrc8Wn/W838FVyxh6rfhj6Ff9w0PzKiAfDyytltL5pfY0SOirVHkMdkmvLIoM= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR06MB5782 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: 1788315449794154100 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. 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 Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: C=C3=A9dric Le Goater --- 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 ba= nk + * 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 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) +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 =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, 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 =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); + uint32_t reg =3D offset >> 2; + uint32_t val; + + val =3D 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 =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_IER: + case R_UDC_EP_ACK_IER: + case R_UDC_EP_NAK_IER: + s->regs[reg] =3D 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] &=3D ~val; + aspeed_udc_update_irq(s); + break; + default: + s->regs[reg] =3D val; + break; + } +} + +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; + uint32_t reg =3D offset >> 2; + uint32_t val; + + val =3D 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 =3D opaque; + uint32_t reg =3D offset >> 2; + + trace_aspeed_udc_ep_write(e->index, offset, data); + e->regs[reg] =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_reset_hold(Object *obj, ResetType type) +{ + AspeedUDCState *s =3D ASPEED_UDC(obj); + int i; + + memset(s->regs, 0, sizeof(s->regs)); + for (i =3D 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] =3D (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 =3D SYS_BUS_DEVICE(dev); + AspeedUDCState *s =3D 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 =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; + 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 =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->desc =3D "ASPEED USB Device Controller"; + dc->realize =3D aspeed_udc_realize; + 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/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')) =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 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_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, uint32_t value) "offset 0x%" PRIx64 " val= ue 0x%x" +aspeed_udc_write(uint64_t offset, uint32_t value) "offset 0x%" PRIx64 " va= lue 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, offse= t 0x%" PRIx64 " value 0x%x" +aspeed_udc_irq(uint32_t isr, uint32_t ier, int level) "isr 0x%x, ier 0x%x,= level %d" --=20 2.53.0 From nobody Sat Sep 26 22:14: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=1788315437; cv=pass; d=zohomail.com; s=zohoarc; b=nEa/F7QkhossBilwEr+9XvDjybsL2fpx2DJwahz+VVH4hypST8k9MqCUKzywDcKJWgRHEddvMWmTk3SV0S6Jc1CbYudftaRIKHkZgk3MDG2bu0uWhihKKHYTPkUQRpCM7hSwCzuRRJUV2eEzoMVhK5c2iYRCbG9woOCbXji/Llg= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788315437; 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=FfQY6C6vfslWQdBdt1WPcfNF9U8o2FRdbaFaPxsd5oo=; b=LZXdDQqr7XCD84+RhXyBVUnBbczT5B0//RLMa1vsJWgQwtBzwcCsbFs62PhfjHZQWjXxSJ0SSi+tNB2roM0SiRITw20M8pK/dzJvQBbrulKyYBl9G5MDRHNWbuHZc90pU9f/gMASzMBz0GONmzFheuEIbVuvPCp8HYnGiigaxsk= 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 1788315437018903.4169092684991; Tue, 1 Sep 2026 19:17:17 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1aWD-0003MM-8C; Tue, 01 Sep 2026 22:16:05 -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 1x1aWB-0003Lp-Rn; Tue, 01 Sep 2026 22:16:03 -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 1x1aW8-0007bI-Mi; Tue, 01 Sep 2026 22:16:03 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by TYZPR06MB5782.apcprd06.prod.outlook.com (2603:1096:400:26a::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Wed, 2 Sep 2026 02:15:45 +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.0360.008; Wed, 2 Sep 2026 02:15:45 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=LsYXzPwGQ6M6B9+RBMam2xZqJsTgGQx01e4eX2degjneLZk3kOKcgMzyAEUIXXqMWbUTCbtdBEGAik0BAAhlT9vwtDDTL8z7slVxFmdkPKfwKFESQeQH7wlo6cTQ5cxS4zwkcxLDxkdpRcjAHBMpo08rICcP6yW5FvYFfIabNh12i5Hgvm6fbHKOV6ZTeXF06G2BwA9sKCvEooMQQ8yXexxxQG8FntDUQTT9JJ+9m6iTTp6Tzzptepzt+j9AlUtEZG4c0f3skm/tILI1CfoxwgcyIrzxj04eiNTVXuEg5oPs9tCi39/3rK0+/+ubn7969dxdwTQ5i9C5hSAmZiqPCg== 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=FfQY6C6vfslWQdBdt1WPcfNF9U8o2FRdbaFaPxsd5oo=; b=KMJOE97g4Alssd7h6XN8GQtQsOwI4US3UzbwjHrnsUBKRiw6XCX7d0rrZt7zgSjb5d8RypIuZj5eS1DnM6CGL5b0Nl0rJh2qL03VAhJkrYz+AT72cOsEtVI/cb33BORcWMcS4XgkaH2bkGQXlWWYKF0/XTxmY2gm81lop5SxztyCATI7hjLChwmSmPYpb+OnRAfRQj9jDqgbdmksVHGf9aP30V/hqdayfQ92Xx9wP0KdICQrmQhOZoAh1v2Qn6fb6utJ0NTuDeLtQpSVUkOVUWljUbI93HOWH2SkT+RcPuTHMLmBLag/xtXTg1AVaqn9h73tquADLLP/NR5e+Kvtcg== 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=FfQY6C6vfslWQdBdt1WPcfNF9U8o2FRdbaFaPxsd5oo=; b=HI6XlZSy4Cks9uawuMuOdEK5btw3cuMgo4aDFU3uE9rsjmZdJRg4fLpX5cEJycoQ+kTn4RKzAIkGEWYJTXitv5HKo4OJCfcCYEvLrSjwhQjtrdbA//bJhIjVeUVoUGMSb26NYz69obB6T6fKRiS8X5j/6J0Wxw/CgWFfI/W3M2yuXd1ngL152hxixWeuGJGvP863xNIAYnCXzGSKAPref3Q9Uw9ZK+xvbhSqm4NvLY8SWskpophWAUVZ0Sp7Owd7FiQr5At9A5lufwb0WYEPedTof2KfvCoO5ZgEPSTcEVr4xkGnKyZ6opI06j08dWpDdy7wpmuJPMJ1vft1pAM0lg== 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 , =?iso-8859-1?Q?Philippe_Mathieu-Daud=E9?= Subject: [PATCH v5 2/4] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Thread-Topic: [PATCH v5 2/4] hw/usb/aspeed-udc: Add ASPEED UDC gadget USB device Thread-Index: AQHdOoD2ANVCOsJJdUWdiqLWx6lqsA== Date: Wed, 2 Sep 2026 02:15:45 +0000 Message-ID: <20260902021542.3194812-3-jamin_lin@aspeedtech.com> References: <20260902021542.3194812-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260902021542.3194812-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_|TYZPR06MB5782:EE_ x-ms-office365-filtering-correlation-id: bbfeef49-eb08-4d6d-5564-08df089818d4 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|1800799024|376014|366016|23010399003|3023799007|10067099003|5023799004|56012099006|921020|6133799003|38070700021|18002099003|22082099003; x-microsoft-antispam-message-info: kW1u8w8jQulHfNAyOlhBSLpDZfvT/cSsmzWAmHIW5Rx5wbO2iSWy1fXOdn0/YTbd/+2j7FKlSI409jfHXQzXjfq61x9JZEBU8B+HLAxDq9txB8680PFwNdmD+tkhgRaiWApWbC+HMdpVqKbdy9JJbQ/GUUIXTK2i2TsEG0LdQbUEI/ukc9F9G79uMIuTPYrNFdIIyUvBPD/Fke4XN+DigM1dq5mwQl6Klm9V26knD2QYNXF62C7/YUwYADxaxPDMP0M7vFp7OxFtwRPCGzfcQY6X+Mst+zcLJHQ4GX8me0tiB3e+otMGN9ke239Ot0o8BCFYccKalBCyQgJndLgZhpBUOqo+xm+kV1t9JFgKtSdmDL2y4cVU3PL4WRK/t15lXQWNFzqWL4P0eJNH55MxCkETQsOglQuCYTgabF6r/dLCk5EJ/jzGhVu7ioX7QMPOO6ZXFYcMenTOZbCYvzb5qLazQ0aTXuxmtOD3WtA5OV0Z2TFoUmvj2Trp5U7RRzZ9li12zzuDl/FVk8SaJWjZpLd7b5PnjzB3S4KXbtV/ibDSUd2+SvjaoXlxzu6FjoChEACfbbv65qFepz6Qyrqf3yPGn/sSxMTZrwVwLyTHtCmTOrJ1J9GzmqYxw4b/cuEDKncsMTajyPvCYg4yl2XY0LIZMywMWkyX6pU5nCuK64iPXP6TNxysucWTmzkj19kbPzdx719uJuHzwb/BJCCpu0dRL7VkGjWtPD1FN4TJqVG8aXiiA3GeHofPBl1vg6bV 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)(1800799024)(376014)(366016)(23010399003)(3023799007)(10067099003)(5023799004)(56012099006)(921020)(6133799003)(38070700021)(18002099003)(22082099003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?N89HajX2+9aSqWzlNhaxdA1sqhzgLr8rcTmHTomUUXMx7+v4tJ5ZMpr5TE?= =?iso-8859-1?Q?rv880auAiZ3KyxYRc3eUcESByKOMw8V8tEr30e+ch5Y5SPZR/6Djak0p86?= =?iso-8859-1?Q?4MnN6jHn7NjLegKKqoP3gOVZCHzD4EfNawPhnrV3KaYz4ZH+FiX+iTeSHN?= =?iso-8859-1?Q?2dA7ux8qV/reIP0F2IWV6TlFq4wha96LOydY11DBxsTgH8zHme2DytVP8j?= =?iso-8859-1?Q?gSB+7aujJq/J7kTwqBLp0hnp3v39zGT+k6tmByHShJgfqXgaU9HjXP0+dR?= =?iso-8859-1?Q?CkVEO3XcBR3u+0U/Y6JicGP9L2cCLLSgAAUSb5Rw3dMxs2nUqfp01TmwRH?= =?iso-8859-1?Q?JJ+uDkqk2zJuZpWrAfiU507Di11RlpRe+xWAT/R/7XkMTwmmHKH00evMrP?= =?iso-8859-1?Q?pvAfwKVIJ/8Czm6Tumj1jm3BD2DWzzly0Bfxio+QyT7EV0GzyUF41hC3BE?= =?iso-8859-1?Q?IZ6YkfpYDWNT6MaB3Gqi99so5SjgJgvJyMKA/TpABi4V8AMhVnrsKIQoae?= =?iso-8859-1?Q?Iq3T4wczra/jKUogAQ+MJM9i+KZNtGl9r4IjY8GtdlaF8qexRgCuedS4d8?= =?iso-8859-1?Q?gEfuUEsNZaiyskG4LHSt6D0rqsMO2fHrL9mZuV2sDueaowmIdbmCD7OjBF?= =?iso-8859-1?Q?HdOC7O/RnpmZ6Xg33L01npbOZH0xWunGMlCDXr+gdoONQKHEUqi3LGN8PX?= =?iso-8859-1?Q?1B0s1KmLSXXamT0bP0/K49LPDvsdBDVYD4hHNSehBOmFj8yTvl7lr+66dR?= =?iso-8859-1?Q?14MAVmQpHe1h7/jYGnSj0KX4uceOkd6hSHIX6d07wYSq6xL5idmg1Gz8U2?= =?iso-8859-1?Q?XZLuEiFLbBMCozarmDHXqE3+GjDT5FWcBch0ZP0kaFBtOufcTyPne4fOEU?= =?iso-8859-1?Q?9l/S+az4Y4LNaZd8AAmckXKCEQvUyaRKAJKvq/f3qIEA37vP/XsmLFaFTW?= =?iso-8859-1?Q?pS8/4BRVo1rM4deMQR3whUMIWECaoDf5Ir6vTrmko8FxPMw5P3F88N7BVi?= =?iso-8859-1?Q?CgL8rwMR9d0Xr/DZCWHi+FmIpsYnrT2OuU+FXSihpVcsgathWyf4SnayrR?= =?iso-8859-1?Q?WSGT79A24AKT7ooAZ8gLYjJYKZlinpof1dWA+/v2TzsmACWftQ3LMmw4wB?= =?iso-8859-1?Q?9tBvMeI80honcofMhIveFxUhL41HQxRQx/NGxRPxf9Ps5mZP1XLa55Y7SW?= =?iso-8859-1?Q?6Z21kyslM10PI65rU+XyP1HOMgxeNC/TneI2YJL2G+mXAaRVd+HAeEbmLl?= =?iso-8859-1?Q?lI+PY0qexpkEcIl9TWdnp+GLwlP2XV25F0KKzF/+ydJUhEUx1KG5qybwt+?= =?iso-8859-1?Q?cuMM1MMrkaErUCAqIfuDB5b2MfWylHHg7gzwDKnZ4C9Y2rB4RIQf5ibp1K?= =?iso-8859-1?Q?xchstrD/ZHvxRMKCp8KqFwBG4uC34t3eAXH1QsRhzoc0vUrW76rjfXMCqP?= =?iso-8859-1?Q?GRH65WxelzN0LZQrCL0bS8Q6pBgyKAHwwIpuRyPN7kY92y47iNBV0jVCdT?= =?iso-8859-1?Q?PjV+3mn/C506HcBN4HMM/xVGyyW4spbv/UFLmrOwo26OmoDvZQ7/pRyiDL?= =?iso-8859-1?Q?k+5ZcTcGIcV9f1Jub0x8/gvspjR3yLUYhBfal1OBGtH4b2sda2TWlHaf8Z?= =?iso-8859-1?Q?jOY4/AbwI1/lsOajQtsfFkq1zLhoyYf7u9OVDAzLLFROuDWigqduWHAjBT?= =?iso-8859-1?Q?uEdbASVerP3xgFhgtODEhad1JFECidbjZLgKovMyalp/qaaLMppQKmX3rv?= =?iso-8859-1?Q?+e0gfspe247jaC2ixD2XkSItY4e9ezozlDVQw3/GEGqO1VKWvLbewsEXbm?= =?iso-8859-1?Q?Xrkbu42k2A=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: KD7MFl/rDVWqFzetOJ/UzvLaxaZ2Drt3LyPvXMFFPU+Wjby4EDJi+1EE+SBIGcbpjvdm8sl+/nChVrznPAw2UWP7mUZBvsgAWjL+d6Arq59wl3b538cCDE6/RrCWmZjuJ3wfDr3HcbZEPMDzl5UPGStF0m1dSMiStpAayUjoD8YNSRNsCpTILcCD5mW/GXbyyLhgEaHSevOBJSgMU5ADV5Vbu6ufd2TGPpDRHfvsaqRCmmy6tQceA2dg/4mYz7UBsu6UQdjdjsC3cPE/W8NotC+OrqL58IRTMW9ZVkS9ezMIZrGFcxiaD1h1YJHvXHep/g7nMOEKZ1Lj1RgXcZgHlg== 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: bbfeef49-eb08-4d6d-5564-08df089818d4 X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Sep 2026 02:15:45.3909 (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: zolVYIgk98SLAubCyfsV29qqCXqd4hFLmqdq+Nf+wUcmJgC26RO2LafJoFEkO1YT2LfaByI7RJBJM6aFbSRzDNh48yxxoM1FNlkDVdfIE/I= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR06MB5782 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: 1788315438381158500 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 links back to its controller through the "udc" property. This patch implements the control endpoint (EP0), which is enough for the host to enumerate the gadget. Host control transfers are handled asynchronously: the SETUP packet is mirrored into the SETUP data buffer, the EP0 interrupt is raised and the host packet is parked (USB_RET_ASYNC). The guest gadget driver then drives the data and status stages by writing UDC_EP0_CTRL; that moves data to/from the driver's DMA buffer and completes the parked packet back to the host. SET_ADDRESS is the exception: it is applied synchronously, because the host controller keeps the transfer bound to address 0 until it completes. The gadget connects to / disconnects from the host bus when the driver sets or clears the upstream-enable (pull-up) bit, and is detached on reset. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daud=C3=A9 --- include/hw/usb/aspeed-udc.h | 28 +++ hw/usb/aspeed-udc.c | 382 +++++++++++++++++++++++++++++++++++- hw/usb/Kconfig | 1 + hw/usb/trace-events | 5 + 4 files changed, 413 insertions(+), 3 deletions(-) diff --git a/include/hw/usb/aspeed-udc.h b/include/hw/usb/aspeed-udc.h index 58fed5f9a2..ab9d016c61 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) + /* * Register map: root/global block at 0x000 - 0x087, then one 0x10 byte ba= nk * per programmable endpoint from 0x200. @@ -36,14 +44,34 @@ typedef struct AspeedUDCEP { int index; } AspeedUDCEP; =20 +struct AspeedUDCGadget { + USBDevice parent_obj; + AspeedUDCState *udc; +}; + struct AspeedUDCState { SysBusDevice parent_obj; =20 MemoryRegion udc_container; MemoryRegion root_mr; + MemoryRegion *dram_mr; + AddressSpace dram_as; uint32_t regs[ASPEED_UDC_ROOT_NR_REGS]; AspeedUDCEP ep[ASPEED_UDC_NUM_EP]; qemu_irq irq; + + /* gadget USB device bound to this controller (set at its realize) */ + AspeedUDCGadget *usbgadget; + + /* + * In-flight EP0 control transfer (host side), deferred until the guest + * gadget driver responds via MMIO. + */ + USBPacket *ep0_packet; + uint32_t ep0_setup_len; + uint32_t ep0_offset; + uint8_t *ep0_data; + bool ep0_dir_in; }; =20 #endif /* HW_USB_ASPEED_UDC_H */ diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c index cd773e979b..70a2296062 100644 --- a/hw/usb/aspeed-udc.c +++ b/hw/usb/aspeed-udc.c @@ -8,16 +8,26 @@ * 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 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; + * - a USBDevice presented on a host controller's bus, which forwards ho= st + * transactions to the guest gadget driver by raising the matching + * controller interrupts and completes them once the guest gadget driv= er + * responds via MMIO. */ =20 #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/log.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 /* Root / Global registers (offset from the controller base) */ @@ -47,6 +57,7 @@ REG32(UDC_EP0_CTRL, 0x30) FIELD(UDC_EP0_CTRL, TX_RDY, 1, 1) FIELD(UDC_EP0_CTRL, STALL, 0, 1) REG32(UDC_EP0_DATA_BUFF, 0x34) + FIELD(UDC_EP0_DATA_BUFF, BASE_ADDR, 0, 31) /* EP0 SETUP packet buffer: SETUP0 =3D bytes 0...3, SETUP1 =3D bytes 4...7= */ REG32(UDC_SETUP0, 0x80) REG32(UDC_SETUP1, 0x84) @@ -66,6 +77,8 @@ REG32(EP_DMA_STS, 0x0C) FIELD(EP_DMA_STS, RPTR, 8, 8) FIELD(EP_DMA_STS, WPTR, 0, 8) =20 +#define ASPEED_UDC_EP0_MAXPKT 64 + static void aspeed_udc_update_irq(AspeedUDCState *s) { bool level; @@ -78,6 +91,176 @@ 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); +} + +/* + * System bus device: MMIO register interface (guest gadget-driver facing) + */ + +/* Connect/disconnect the gadget device from the host bus */ +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); +} + +static void aspeed_udc_ep0_tx_ready(AspeedUDCState *s, uint32_t val) +{ + uint32_t txlen =3D FIELD_EX32(val, UDC_EP0_CTRL, TX_LEN); + uint32_t data_buf_addr =3D s->regs[R_UDC_EP0_DATA_BUFF]; + USBPacket *p; + uint32_t n; + + if (!s->ep0_dir_in) { + /* Status stage IN (zero length) for an OUT / no-data transfer */ + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_IN_ACK_MASK); + aspeed_udc_ep0_complete(s, s->ep0_offset); + return; + } + /* no control transfer is waiting: nothing to send */ + if (!s->ep0_packet) { + return; + } + + /* IN data stage: copy from the guest gadget driver's DMA buffer */ + n =3D MIN(txlen, s->ep0_setup_len - s->ep0_offset); + if (n && address_space_read(&s->dram_as, data_buf_addr, + MEMTXATTRS_UNSPECIFIED, + s->ep0_data + s->ep0_offset, + n) !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: EP0 IN DMA read failed\n", __func__); + p =3D s->ep0_packet; + s->ep0_packet =3D NULL; + p->status =3D USB_RET_IOERROR; + usb_generic_async_ctrl_complete(USB_DEVICE(s->usbgadget), p); + return; + } + s->ep0_offset +=3D n; + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_IN_ACK_MASK); + if (txlen < ASPEED_UDC_EP0_MAXPKT || s->ep0_offset >=3D s->ep0_setup_l= en) { + aspeed_udc_ep0_complete(s, s->ep0_offset); + } +} + +static void aspeed_udc_ep0_rx_ready(AspeedUDCState *s) +{ + uint32_t data_buf_addr =3D s->regs[R_UDC_EP0_DATA_BUFF]; + USBPacket *p; + uint32_t n; + + if (s->ep0_dir_in) { + /* Status stage OUT (zero length) for an IN transfer */ + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_OUT_ACK_MASK); + return; + } + /* no control transfer is waiting: nothing to receive */ + if (!s->ep0_packet) { + return; + } + + /* OUT data stage: hand host data to the guest gadget driver */ + n =3D MIN(s->ep0_setup_len - s->ep0_offset, ASPEED_UDC_EP0_MAXPKT); + if (n && address_space_write(&s->dram_as, data_buf_addr, + MEMTXATTRS_UNSPECIFIED, + s->ep0_data + s->ep0_offset, + n) !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: EP0 OUT DMA write failed\n", __func__); + p =3D s->ep0_packet; + s->ep0_packet =3D NULL; + p->status =3D USB_RET_IOERROR; + usb_generic_async_ctrl_complete(USB_DEVICE(s->usbgadget), p); + return; + } + s->ep0_offset +=3D n; + s->regs[R_UDC_EP0_CTRL] =3D FIELD_DP32(s->regs[R_UDC_EP0_CTRL], + UDC_EP0_CTRL, RX_LEN, n); + aspeed_udc_raise_isr(s, R_UDC_ISR_EP0_OUT_ACK_MASK); +} + +/* + * The guest gadget driver drives EP0 by writing UDC_EP0_CTRL. Translate + * those writes into data movement to/from the deferred host control packet + * plus the matching ACK interrupts the guest gadget driver expects. + */ +static void aspeed_udc_ep0_ctrl_write(AspeedUDCState *s, uint32_t val) +{ + USBPacket *p; + + trace_aspeed_udc_ep0_ctrl_write(val, s->ep0_dir_in, s->ep0_offset); + + if (val & R_UDC_EP0_CTRL_STALL_MASK) { + /* Gadget stalled EP0: fail the pending control transfer */ + if (s->ep0_packet) { + p =3D s->ep0_packet; + s->ep0_packet =3D NULL; + p->status =3D USB_RET_STALL; + usb_generic_async_ctrl_complete(USB_DEVICE(s->usbgadget), p); + } + } else if (val & R_UDC_EP0_CTRL_TX_RDY_MASK) { + s->regs[R_UDC_EP0_CTRL] &=3D ~R_UDC_EP0_CTRL_TX_RDY_MASK; + aspeed_udc_ep0_tx_ready(s, val); + } else if (val & R_UDC_EP0_CTRL_RX_RDY_MASK) { + s->regs[R_UDC_EP0_CTRL] &=3D ~R_UDC_EP0_CTRL_RX_RDY_MASK; + aspeed_udc_ep0_rx_ready(s); + } +} + +/* The upstream-enable bit connects/disconnects the gadget device */ +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); + + 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); @@ -100,6 +283,11 @@ static void aspeed_udc_write(void *opaque, hwaddr offs= et, uint64_t data, trace_aspeed_udc_write(offset, val); =20 switch (reg) { + case R_UDC_FUNC_CTRL: + val &=3D 0x000e1fff; + aspeed_udc_func_ctrl_write(s, val); + s->regs[R_UDC_FUNC_CTRL] =3D val; + break; case R_UDC_IER: case R_UDC_EP_ACK_IER: case R_UDC_EP_NAK_IER: @@ -112,6 +300,16 @@ static void aspeed_udc_write(void *opaque, hwaddr offs= et, uint64_t data, s->regs[reg] &=3D ~val; aspeed_udc_update_irq(s); break; + case R_UDC_EP0_CTRL: + s->regs[reg] =3D val & (R_UDC_EP0_CTRL_STALL_MASK | + R_UDC_EP0_CTRL_TX_RDY_MASK | + R_UDC_EP0_CTRL_RX_RDY_MASK | + R_UDC_EP0_CTRL_TX_LEN_MASK); + aspeed_udc_ep0_ctrl_write(s, val); + break; + case R_UDC_EP0_DATA_BUFF: + s->regs[reg] =3D val & R_UDC_EP0_DATA_BUFF_BASE_ADDR_MASK; + break; default: s->regs[reg] =3D val; break; @@ -171,6 +369,7 @@ static const MemoryRegionOps aspeed_udc_ep_ops =3D { static void aspeed_udc_reset_hold(Object *obj, ResetType type) { AspeedUDCState *s =3D ASPEED_UDC(obj); + USBDevice *udev; int i; =20 memset(s->regs, 0, sizeof(s->regs)); @@ -182,6 +381,21 @@ static void aspeed_udc_reset_hold(Object *obj, ResetTy= pe type) s->regs[R_UDC_DEV_RESET] =3D (R_UDC_DEV_RESET_ROOT_MASK | R_UDC_DEV_RESET_DMA_MASK | R_UDC_DEV_RESET_EP_POOL_MASK); + s->ep0_packet =3D NULL; + + /* + * A guest reboot resets the controller but leaves the USB device + * attached to the host bus with no guest gadget driver behind it. + * Detach it, otherwise the rebooted host fails to re-enumerate the + * driverless gadget device; it re-attaches when the new driver asserts + * pull-up. + */ + if (s->usbgadget) { + udev =3D USB_DEVICE(s->usbgadget); + if (udev->attached) { + usb_device_detach(udev); + } + } } =20 static void aspeed_udc_realize(DeviceState *dev, Error **errp) @@ -190,6 +404,12 @@ static void aspeed_udc_realize(DeviceState *dev, Error= **errp) AspeedUDCState *s =3D ASPEED_UDC(dev); int i; =20 + if (!s->dram_mr) { + error_setg(errp, TYPE_ASPEED_UDC ": 'dram' link not set"); + return; + } + address_space_init(&s->dram_as, s->dram_mr, "dram"); + 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, @@ -214,6 +434,20 @@ static void aspeed_udc_realize(DeviceState *dev, Error= **errp) sysbus_init_irq(sbd, &s->irq); } =20 +static const Property aspeed_udc_properties[] =3D { + DEFINE_PROP_LINK("dram", AspeedUDCState, dram_mr, + TYPE_MEMORY_REGION, MemoryRegion *), +}; + +static void aspeed_udc_init(Object *obj) +{ + AspeedUDCState *s =3D ASPEED_UDC(obj); + + object_property_add_link(obj, "usbgadget", TYPE_ASPEED_UDC_GADGET, + (Object **)&s->usbgadget, + object_property_allow_set_link, 0); +} + static void aspeed_udc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); @@ -222,6 +456,141 @@ static void aspeed_udc_class_init(ObjectClass *klass,= const void *data) dc->desc =3D "ASPEED USB Device Controller"; dc->realize =3D aspeed_udc_realize; rc->phases.hold =3D aspeed_udc_reset_hold; + device_class_set_props(dc, aspeed_udc_properties); +} + +/* + * USB device: gadget device 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 guest gadget + * driver expects, then defer (USB_RET_ASYNC) until the driver responds + * through the MMIO register interface above. + */ + +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; + /* The EHCI host is High-Speed; advertise it to the guest gadget drive= r */ + 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 req =3D request & 0xff; + uint8_t type =3D request >> 8; + + /* + * Reconstruct the 8-byte SETUP packet into the SETUP data buffer where + * the guest gadget driver reads it from. + */ + s->regs[R_UDC_SETUP0] =3D type | (req << 8) | ((value & 0xffff) << 16); + s->regs[R_UDC_SETUP1] =3D (index & 0xffff) | ((length & 0xffff) << 16); + + /* A new SETUP clears the EP0 STALL condition */ + s->regs[R_UDC_EP0_CTRL] &=3D ~R_UDC_EP0_CTRL_STALL_MASK; + + 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 is delivered while the device still answers at the defa= ult + * address 0 and carries the new address in wValue. The host controller + * keeps this transfer's queue bound to address 0 until it completes, = so + * apply the new address synchronously as the transfer completes. + * Completing it asynchronously (USB_RET_ASYNC) would change udev->addr + * while the queue is still bound to 0; the host controller sees the + * mismatch, tears the queue down and enumeration breaks. The guest ga= dget + * driver is still notified so its state machine advances. + */ + 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) +{ + /* Programmable endpoint (bulk) transfers are added in a later patch. = */ + p->status =3D USB_RET_STALL; +} + +static void aspeed_udc_gadget_cancel_packet(USBDevice *udev, USBPacket *p) +{ + AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + + if (s->ep0_packet =3D=3D p) { + s->ep0_packet =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, TYPE_ASPEED_UDC_GADGET ": 'udc' link is not set"); + return; + } + /* Bind this gadget to its controller through the link property */ + object_property_set_link(OBJECT(dev->udc), "usbgadget", OBJECT(dev), + &error_abort); + + 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 void aspeed_udc_gadget_unrealize(USBDevice *udev) +{ + AspeedUDCGadget *dev =3D ASPEED_UDC_GADGET(udev); + + if (dev->udc && dev->udc->usbgadget =3D=3D dev) { + object_property_set_link(OBJECT(dev->udc), "usbgadget", NULL, + &error_abort); + } +} + +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->unrealize =3D aspeed_udc_gadget_unrealize; + 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); } =20 static const TypeInfo aspeed_udc_types[] =3D { @@ -229,8 +598,15 @@ static const TypeInfo aspeed_udc_types[] =3D { .name =3D TYPE_ASPEED_UDC, .parent =3D TYPE_SYS_BUS_DEVICE, .instance_size =3D sizeof(AspeedUDCState), + .instance_init =3D aspeed_udc_init, .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/Kconfig b/hw/usb/Kconfig index e4df15fe7c..e8c00f813a 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -149,3 +149,4 @@ config USB_CHIPIDEA =20 config ASPEED_UDC bool + select USB diff --git a/hw/usb/trace-events b/hw/usb/trace-events index ed05304520..098c3d6179 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -383,4 +383,9 @@ aspeed_udc_read(uint64_t offset, uint32_t value) "offse= t 0x%" PRIx64 " value 0x% aspeed_udc_write(uint64_t offset, uint32_t value) "offset 0x%" PRIx64 " va= lue 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, offse= t 0x%" PRIx64 " value 0x%x" +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" --=20 2.53.0 From nobody Sat Sep 26 22:14: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=1788315432; cv=pass; d=zohomail.com; s=zohoarc; b=MCkrJxY2Jx4oKEgMtij8XmPILC9xW3dgHb+GXLHKcgG49Xyb3X5HqckkoUc0uH3EoIJXmr1GjpeGpf/8sQ1bdaLLJBs8czlN54Zsh3GzPUmtaF4+V5perlntu53g+GfIV1J1gGtSIU7pqi5NqWRnX8VL06J9AH0cX6uagLlCzx8= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788315432; 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=B9XpiosTGyTyN8N4hS6y2XVf89/dJ/gpFInmUqB+UvE=; b=l7j3PrQ92Hc/gl6jD7iV1aaEFsZh1cwg1IiAMHJxOE11DimLVuhOKAR2PQND6HCsC43Gk9O7IbDmuCXhgFCeUtKIo74prl17l4euLOlQgSsyCopbtHz6jJXadk3ya1yOU7/b5x6N52d+S+OUeuY3zWvIEwuVN/bGG+Hzgb/msss= 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 1788315431848662.7872600922256; Tue, 1 Sep 2026 19:17:11 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1aWK-0003OV-8c; Tue, 01 Sep 2026 22:16:12 -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 1x1aWJ-0003OG-2p; Tue, 01 Sep 2026 22:16:11 -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 1x1aWG-0007bI-2V; Tue, 01 Sep 2026 22:16:10 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by TYZPR06MB5782.apcprd06.prod.outlook.com (2603:1096:400:26a::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Wed, 2 Sep 2026 02:15:46 +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.0360.008; Wed, 2 Sep 2026 02:15:46 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=lDgbdja/mNLfdzkjrJhQz+6gLQBq/8H3FhJ+Eo3Mqj4pdBv5oWCiVvAHdTwP/4WdVmXdus7jTg0VUBC6H1qCkEcIcOGQwX6ux2j3YVy46lTcpW9boR8OaOo5gDcs0YX89psU6Tki5xzeNBYqkz3glBfeWgBVN+8K3RvVej+thfseg6aGJXXnBNJEOp8+8rUfUAhzHmiuf3yFEwXfUPEFVl7dtGm2A7cBLXlB7DFh5BX8MaRZ6UiIT/8V/xsVZTFt3n/fsQit30yt7TGQqjKxyr0A9CE5tuV18g2xfZo3BqFLAbPrAt/nzqRI1hT9dj0NTKu0a4GGkyWL/+/KwTLdQA== 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=B9XpiosTGyTyN8N4hS6y2XVf89/dJ/gpFInmUqB+UvE=; b=CJZM9pGzoQUJFHvsLxGz1CmRmH5aqAChptZN0Jxlihcxz6JoSfRVlOGrxFdJegETCQtG47p8g/bpykVIasbhQHhcHfteFs9ApYGHVPQ8qxtbUnQYftIOd/gjwSsc8derve/4UxpCcTW3uTejHnQFnb1LKAlxlcM+LwMSbfh8fCyorj3CSavXquqwIrVAl/ae+CUNssWVikGSCEXw1yfn3Bk5wuLGbe8C2+HqH6P4PSJoM4zyNZY8gcHcjRG+fiD2cTKE2L77YIqEVe4GYzoQ7MxBW185tLGVpz+bfynwYnhhXOBVd+diKb8Gs3oQBGCB/DE+Endwtzf9o9d9gHdjIw== 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=B9XpiosTGyTyN8N4hS6y2XVf89/dJ/gpFInmUqB+UvE=; b=JHgCI69I2rqhxfvu8UTBQhBdsLr3ZDdfUf4wjRDr2lL8j3d+RhYiWCaPcnZYUXGvveNWlF9k10W8fR4F2kPLOq6rPkTTjlra/Wfr9TWn4MwiRi5c5SSahueeMnBFgohP/qgNcRbUjQWICWncNoISUlSFTFUXqo/ElP8BMTdOWRJj6ltosEU7QF3CuXlEXV7ckgvZ4bBpUlPUjqrcqbEFJkAu3d2KHUJjJN27ma734PyMToUZggzWbOwOmmd57AP3oiPuvQIEF0s++g8+P6E3V1sRqSspwYEosFQT8Srj4zgm+2L7pGs6ljD9v+nxGrVHe/lOgtDkHNvzwrVcQotSNg== 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 v5 3/4] hw/usb/aspeed-udc: Add programmable endpoint DMA transfers Thread-Topic: [PATCH v5 3/4] hw/usb/aspeed-udc: Add programmable endpoint DMA transfers Thread-Index: AQHdOoD37kjm5oF7RUyC4r+ErJNbUw== Date: Wed, 2 Sep 2026 02:15:46 +0000 Message-ID: <20260902021542.3194812-4-jamin_lin@aspeedtech.com> References: <20260902021542.3194812-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260902021542.3194812-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_|TYZPR06MB5782:EE_ x-ms-office365-filtering-correlation-id: ce1bafee-43c5-4d07-2270-08df08981982 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|1800799024|376014|366016|23010399003|3023799007|10067099003|5023799004|56012099006|921020|6133799003|38070700021|18002099003|22082099003; x-microsoft-antispam-message-info: InxTxAumywbcmU2e5avAfblLrnnMIYp+hBlA+qK6x7zXzFXMmsK8f00vksKCKw2ZGbXNK500p4/op5XBnx4/gGLE0BRB4D8UVCALQHAFBFLMFvkQGz2U3YazBWlbfsLCuAP8moc6zLDlcCX3ho+qAwqGYrMzyltiPCH+twQnw8AsCrsGmAJI01wo+TAaE5rXH2vuulUpqwkHS5719BF1mrPXdTxnbZrq9V9I1RskQnzCdDJZ+LPAHOTHFqW1WoO6enkflbcf4cFQYfSVQ+kM7tOZsk/Xk0e/OTe3EtQ2qJESjZzlcvJClWpkofl75Vw1kNOCT2qF5Hilt4zT2K2dqz6Zj0FkJsisIM9BrQeVXZi0HvqJIN4zzI56RpoOq8oIngpO/XFgHs28GZ2S3UQ0k5BamxHaiv1Xxw7AMtjSpRO/XdjoKNrG1afdsLLgFlAVvDxmtiYItPhazt4K+wQpxBizzpkw8zxXmIdlxT3kJKowpnqUjs/l+++bsk4mRVPELSXqVb5GBcuE/2wE4cNLlUQJYmJL9emwghWk8Vyx8ZOd7z8f0qL3GJ7FgKpYGs1Z0lYNFb6+EtT7zEVZxp7CObF9o9/yNS47MiaD+X7li7pYYA7/BIaKtkh+9cHlo6k9YDkN/9AXhdBCc5W/fFmS0RHt2ZGh8mNl3HPo1J/A5a5mSis1YhBR6bCVxvvNdfHVAgPcIQQtdPxU4jxIxboclb5KTycAhL/YF8nvzRM2FfIZFSs+1rcOZiy4dlBrB1GW 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)(1800799024)(376014)(366016)(23010399003)(3023799007)(10067099003)(5023799004)(56012099006)(921020)(6133799003)(38070700021)(18002099003)(22082099003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?cYRkjUPaxB++OxaZpffT5oF56NjZ9pNbNpcbjlvCKV6NfrHvwgDp/LB4zX?= =?iso-8859-1?Q?gxeUkaMbnC+2oXSHCBT1wHMsxZZEvIIcKechjdpNvnRmSGKkDfRLcLuPz6?= =?iso-8859-1?Q?oryiwL/0jS1iH2II5Xu6Ya3X26xv3o90OJOc2OH9IKE5kGPgNnBEBhhmmS?= =?iso-8859-1?Q?AUXgSKZW3Q+LQNBu/I+3UKzXVkYz7LQR4X+TuqulZ1fH+Krs/FzPV9aLvL?= =?iso-8859-1?Q?qqxb47LUQ3x88nBxGTEpxvzAfUvAJJYe4QUrDB2fTici8xwNYiMqTJZTk/?= =?iso-8859-1?Q?lkgEbQ3DVEsMnAITWg13BTphh1o2LlSaKhs693dBWoEmmzniKWCXbesa38?= =?iso-8859-1?Q?2GxEyoAQUvoEKsKkw/Qsdnr+Hhvc1tGDhz9ePSjYqXu+VQx6RRQ6UaC9/V?= =?iso-8859-1?Q?S60fTub3HK7loFA/qPn2odPoxH7hMlPHzNvPtlo377NAKjxH8YYKM0FRAa?= =?iso-8859-1?Q?2eKjHEAomnZYFfNn65lagGz3iBHjeqe5LoGj5oala1r7xDduSuTune/oUP?= =?iso-8859-1?Q?jkSWpdWmZSaLQfW2qQcCied1CsOkgSvUbLp02ixHU08tbbrlmf3eGqjMa7?= =?iso-8859-1?Q?Kc9GjQZQT+OfsBIG5KxbVlCdtfjvpl7o6oKu0W0onXYCZj1omSRTO4xxKt?= =?iso-8859-1?Q?RGHLkbuIXMgsDJajsK16VniMzU34mhO4uB0iPeKX70VkCyUZ9JMscWpDKM?= =?iso-8859-1?Q?CeTTvMsGfSUUo1tNrWpUt0jkdYLRDPZEtRPyZWC7Ag4gfqJjI5qCFkuh69?= =?iso-8859-1?Q?EQpTJeBue7x4qCu4JMTuV/XrfG3yaeHHmaPld8QzpGG6ODAqV/nmcTlNhq?= =?iso-8859-1?Q?z8kjCHvNl6k5vpIOE2PeSZarmdqpSz11vTaB/FbKlz8c/LcIbzUJPdT3ep?= =?iso-8859-1?Q?5+MIfZED2nGsLp2Ft9AJJGevhUWCrTYBN1ynh109YgbjEJnM5iTYZGtEDA?= =?iso-8859-1?Q?G2lA32h8bQtH0NOz2f0ZKDO2oFdBpqOk2Dc85vY4bZo+5XwxmVwg6Cqayc?= =?iso-8859-1?Q?vzOPgQ77mAhilcaUBcdFuoz/iwV5RNkZLCsBIEUedjwZGgjTpqxA+tAM6W?= =?iso-8859-1?Q?t5x2atdVLvI7vBmEhqpadBbrOtqifFnKuuXMXKX5CwB9BVFDDxVgQMtV8B?= =?iso-8859-1?Q?yZh0J6LCeMfVy4PJ09mx/zC7T10QQ4kd3asPRBbkfkU3dCyQzqxVfWA04u?= =?iso-8859-1?Q?0Zi/KAhPAqWxHVM5C/yfkVFEXaq6F4IleKEGuL50hBhVpsLBDQFsRfzmHr?= =?iso-8859-1?Q?FwywpiTqW53Ry//J/DSXyvuhbPu8rJ11wXoyMDRchBQKpD3Y+3ShCkhLYn?= =?iso-8859-1?Q?45sGjYXyukepnl9XkCoHDL/GNITvBEemVc1tYfhINdlL9zE4dv+puMN9DL?= =?iso-8859-1?Q?JVEJSnnY6DIDlX/3t0Kaib8FZQbvXmbEuvNAfynvDDBP7tX4FUkAJZwVZ0?= =?iso-8859-1?Q?YyAU6mcu8e5MC0oqj7h2ISTzSiGlRvLgurVQNdFYKPvlz5ZOKMHwbcS/c/?= =?iso-8859-1?Q?6D7DRxJtMrgeb7zQQuTufYdMoI+Xv9B5C72IBrPIgXvwjTFx/j+EjB+ee1?= =?iso-8859-1?Q?KxhGv/Q5MjJZ7sCiomPlIMcBryekymysc5KJmpWy0pIw8HxaEHDF15VFKR?= =?iso-8859-1?Q?RgPDMvaPxos3cLQMNqusH6+Y39h8M8+VdZYB/IDHDIVFJahBj8wyk2Pdbq?= =?iso-8859-1?Q?vYb1JVKzcm75+oqoUhWKCa+rCInMIOuqjeuuSdJjZAmfC33O78eT9uOOFW?= =?iso-8859-1?Q?Ad4kq7cSqvtVyqI3L6npRr/0wCjmOruwZvesDsR7QoSCV51SM/Hxq1zKu1?= =?iso-8859-1?Q?uw5eBY2GWQ=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: vPxxQuSiWBI6WsmTiZ6gkS3LozujDcZf5uK2K0VjFUnb9UIX2hH2Rc3dpDzyxQ+wrslnVT1zI9vspS/tNEbEoH7jsiBJsgt+OREQI2VM/QDjQExKOvTR03OeNVNrwfcGD1UqZlElrHTcLH5iUt4ZhhlCXdiepjmWNSHZYbtvIM33oRKOpi1P1s2qyE7tMSbzaH5Guuohh9k2nCTHnEU1Kp/wI3aTGBs2it7qleNoif82eJYrmom9mPZEsCfelbL2J31xUwxIRbeYDka19H1sWPRr9gPaAp3K2Dnwgx34buWM5CYYYTqEvLqnpZNnUGTdnm0JCxyTglWASJGtprLiyw== 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: ce1bafee-43c5-4d07-2270-08df08981982 X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Sep 2026 02:15:46.5619 (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: ZH6IcW0y1e3iJ2FgYZj89k1bjkL8iY7V6curERx+/Kag71uL6GbXuXLAWWAXruPRExMRav+WuCJvAEZh0BJyoXkS/rqnQQELIwenpPuFV7A= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR06MB5782 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: 1788315433950158500 Content-Type: text/plain; charset="utf-8" Add the bulk data plane for the four programmable endpoints. The gadget driver queues IN data through the descriptor-list DMA ring and arms OUT buffers through the single-stage DMA registers; host bulk transactions are served from / delivered into those. The DMA mode is taken from EP_DMA_CTRL.DESC_OP_EN: IN endpoints use the descriptor-list ring, OUT endpoints use single-stage buffers. A transfer larger than one host packet is served across several polls, with the host packet parked (USB_RET_ASYNC) until the gadget queues (IN) or arms (OUT) more data, then completed from the matching DMA kick. With this the gadget data endpoints work, e.g. a mass-storage gadget can be enumerated and read/written end to end. Signed-off-by: Jamin Lin Reviewed-by: C=C3=A9dric Le Goater --- include/hw/usb/aspeed-udc.h | 8 + hw/usb/aspeed-udc.c | 456 +++++++++++++++++++++++++++++++++++- hw/usb/trace-events | 4 + 3 files changed, 463 insertions(+), 5 deletions(-) diff --git a/include/hw/usb/aspeed-udc.h b/include/hw/usb/aspeed-udc.h index ab9d016c61..7701c1aa34 100644 --- a/include/hw/usb/aspeed-udc.h +++ b/include/hw/usb/aspeed-udc.h @@ -42,6 +42,14 @@ typedef struct AspeedUDCEP { MemoryRegion mr; uint32_t regs[ASPEED_UDC_EP_NR_REGS]; int index; + + /* + * host packet parked until the guest gadget driver queues (IN) or + * arms (OUT) data + */ + USBPacket *pkt; + /* bytes of the current IN descriptor already served */ + uint32_t desc_off; } AspeedUDCEP; =20 struct AspeedUDCGadget { diff --git a/hw/usb/aspeed-udc.c b/hw/usb/aspeed-udc.c index 70a2296062..4c90b4f4c2 100644 --- a/hw/usb/aspeed-udc.c +++ b/hw/usb/aspeed-udc.c @@ -72,12 +72,37 @@ 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) + FIELD(EP_DMA_BUFF, BASE_ADDR, 0, 31) 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) =20 -#define ASPEED_UDC_EP0_MAXPKT 64 +#define ASPEED_UDC_EP0_MAXPKT 64 +#define ASPEED_UDC_EP_MAXPKT 1024 + +/* 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 */ +#define EP_DMA_CTRL_STS_RX_IDLE 0x0 +#define EP_DMA_CTRL_STS_TX_IDLE 0x8 + +/* DMA descriptor (DES1) fields, in guest memory */ +#define ASPEED_EP_DESC1_IN_LEN(ctrl) ((ctrl) & 0x1fff) +/* interrupt-on-completion */ +#define ASPEED_EP_DESC1_INTR BIT(31) + +/* Result of moving a host data packet through an endpoint's DMA */ +typedef enum { + /* whole packet transferred */ + ASPEED_UDC_XFER_DONE, + /* not finished, keep parked */ + ASPEED_UDC_XFER_MORE, + /* DMA failed */ + ASPEED_UDC_XFER_ERROR, +} AspeedUDCXferResult; =20 static void aspeed_udc_update_irq(AspeedUDCState *s) { @@ -97,6 +122,14 @@ static void aspeed_udc_raise_isr(AspeedUDCState *s, uin= t32_t mask) aspeed_udc_update_irq(s); } =20 +static void aspeed_udc_raise_ep_ack(AspeedUDCState *s, int 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) */ @@ -330,6 +363,287 @@ static const MemoryRegionOps aspeed_udc_ops =3D { }, }; =20 +/* + * Copy len bytes from guest memory at addr into the IN packet, going thro= ugh + * a bounce buffer one buf-full at a time. Returns false on DMA failure. + */ +static bool aspeed_udc_ep_copy_to_pkt(AspeedUDCState *s, int ep, uint32_t = addr, + uint32_t len, USBPacket *p) +{ + uint8_t buf[ASPEED_UDC_EP_MAXPKT]; + uint32_t copied =3D 0; + uint32_t seg; + + while (copied < len) { + seg =3D MIN(len - copied, sizeof(buf)); + if (address_space_read(&s->dram_as, addr + copied, + MEMTXATTRS_UNSPECIFIED, buf, seg) !=3D MEMT= X_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ep %d IN data DMA read failed\n", __func__, + ep); + return false; + } + usb_packet_copy(p, buf, seg); + copied +=3D seg; + } + + return true; +} + +/* + * IN transfer: send data to the host by filling its IN packet from the + * buffers the guest gadget driver queued in the descriptor ring (from the + * read pointer to the write pointer). + * + * One host packet can be bigger than one descriptor's buffer, so we copy = from + * several descriptors in a row until the packet is full or the ring is em= pty. + * If a descriptor is too big for the space left in the packet, we copy on= ly + * part of it now and copy the rest on the next call; desc_off remembers h= ow + * far we got. We move the read pointer to the next descriptor only after a + * descriptor is fully copied, so the guest gadget driver can read the poi= nter + * and see how much was sent. + * + * This function raises the endpoint ACK by itself when the ring becomes e= mpty + * or when a descriptor asks for an interrupt. + */ +static AspeedUDCXferResult aspeed_udc_ep_xfer_in(AspeedUDCState *s, int ep, + USBPacket *p) +{ + 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 wptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, WPTR); + uint32_t rptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, RPTR); + uint32_t desc_base =3D e->regs[R_EP_DMA_BUFF]; + uint32_t desc_addr; + uint32_t remaining; + uint32_t desc_ctrl; + uint32_t pkt_space; + /* des_0: data buffer base address, des_1: control/status */ + uint32_t desc[2]; + uint32_t offset; + uint32_t chunk; + uint32_t dlen; + bool done =3D false; + bool ack =3D false; + + if (mps =3D=3D 0) { + /* a MAX_PKT field of 0 means the maximum packet size */ + mps =3D ASPEED_UDC_EP_MAXPKT; + } + + trace_aspeed_udc_ep_data_in(ep, rptr, wptr, pktiov->size); + + /* walk the queued descriptors, filling the packet */ + while (rptr !=3D wptr) { + if (address_space_read(&s->dram_as, desc_base + rptr * sizeof(desc= ), + MEMTXATTRS_UNSPECIFIED, desc, + sizeof(desc)) !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ep %d descriptor DMA read failed\n", + __func__, ep); + return ASPEED_UDC_XFER_ERROR; + } + desc_addr =3D le32_to_cpu(desc[0]) & R_EP_DMA_BUFF_BASE_ADDR_MASK; + desc_ctrl =3D le32_to_cpu(desc[1]); + dlen =3D ASPEED_EP_DESC1_IN_LEN(desc_ctrl); + offset =3D e->desc_off; + /* how much to copy: min(descriptor bytes left, packet space left)= */ + remaining =3D dlen > offset ? dlen - offset : 0; + pkt_space =3D pktiov->size > (uint32_t)p->actual_length ? + pktiov->size - (uint32_t)p->actual_length : 0; + chunk =3D MIN(remaining, pkt_space); + + if (!aspeed_udc_ep_copy_to_pkt(s, ep, desc_addr + offset, chunk, p= )) { + return ASPEED_UDC_XFER_ERROR; + } + e->desc_off +=3D chunk; + + if (e->desc_off < dlen) { + /* + * The packet ran out of space in the middle of this descripto= r, + * so only part of it was copied. Stop here, and leave the read + * pointer on this descriptor: the next call resumes copying t= he + * rest (desc_off remembers how far we got). + */ + done =3D true; + break; + } + + /* + * This descriptor was copied in full. Advance the read pointer to= the + * next descriptor and reset desc_off so it starts from the beginn= ing. + */ + rptr =3D (rptr + 1) % ASPEED_UDC_DESCS_COUNT; + e->desc_off =3D 0; + if (desc_ctrl & ASPEED_EP_DESC1_INTR) { + ack =3D true; + } + /* + * This descriptor is shorter than the max packet size, i.e. a sho= rt + * (or zero-length) packet. In USB that marks the end of the trans= fer, + * so stop here. + */ + if (dlen < mps) { + done =3D true; + break; + } + /* + * The packet is now completely full, so the host has received all= the + * data it asked for. Stop here. + */ + if ((uint32_t)p->actual_length >=3D pktiov->size) { + done =3D true; + break; + } + } + + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(e->regs[R_EP_DMA_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 guest gadget driver completes its request when the ring drains = */ + if (rptr =3D=3D wptr) { + ack =3D true; + } + if (ack) { + aspeed_udc_raise_ep_ack(s, ep); + } + + return done ? ASPEED_UDC_XFER_DONE : ASPEED_UDC_XFER_MORE; +} + +/* + * OUT transfer: receive data from the host by copying its OUT packet into= the + * buffer the guest gadget driver set up (single-stage mode). + * + * A host packet can be bigger than one buffer, so we copy at most PKT_SIZE + * bytes per call, continuing from where the last call stopped + * (p->actual_length). The caller keeps the packet parked until it is fully + * copied. + */ +static AspeedUDCXferResult aspeed_udc_ep_xfer_out(AspeedUDCState *s, int e= p, + USBPacket *p) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t chunk =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, PKT_S= IZE); + uint32_t remaining =3D p->iov.size - (uint32_t)p->actual_length; + uint32_t data_buf_addr =3D e->regs[R_EP_DMA_BUFF]; + uint32_t len =3D MIN(remaining, chunk); + g_autofree uint8_t *buf =3D g_malloc(len); + + if (data_buf_addr && len) { + usb_packet_copy(p, buf, len); + if (address_space_write(&s->dram_as, data_buf_addr, + MEMTXATTRS_UNSPECIFIED, buf, + len) !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ep %d OUT data DMA write failed\n", + __func__, ep); + return ASPEED_UDC_XFER_ERROR; + } + } + + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(e->regs[R_EP_DMA_STS], + EP_DMA_STS, PKT_SIZE, len); + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(e->regs[R_EP_DMA_STS], + EP_DMA_STS, WPTR, 0); + 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); + + if ((uint32_t)p->actual_length >=3D p->iov.size) { + return ASPEED_UDC_XFER_DONE; + } + + return ASPEED_UDC_XFER_MORE; +} + +/* + * IN kick: the guest gadget driver wrote EP_DMA_STS to tell us it queued = more + * IN data to send to the host. If a host IN request is already waiting + * (parked because there was no data before), send the data now and finish= it. + * If the request needs more data than was queued, keep it parked and wait= for + * the next kick. + */ +static void aspeed_udc_ep_in_kick(AspeedUDCState *s, int ep, uint32_t val) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t cur_rptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, RP= TR); + uint32_t new_rptr =3D FIELD_EX32(val, EP_DMA_STS, RPTR); + uint32_t new_wptr =3D FIELD_EX32(val, EP_DMA_STS, WPTR); + USBPacket *p =3D e->pkt; + + /* + * A normal kick only sets the write pointer and leaves the read-point= er + * field 0 (the read pointer is ours to advance). The guest resets the= ring + * by writing a read pointer that is non-zero and equal to the write + * pointer. + * + * We check non-zero as well as equal: on a normal kick whose write po= inter + * just wrapped back to 0, both fields would be 0, so an "equal" test = alone + * would look like a reset by mistake. + */ + if (new_rptr !=3D 0 && new_rptr =3D=3D new_wptr) { + cur_rptr =3D new_rptr; + e->desc_off =3D 0; + } + /* store the guest's write, but keep our own read pointer */ + e->regs[R_EP_DMA_STS] =3D FIELD_DP32(val, EP_DMA_STS, RPTR, cur_rptr); + + /* nothing to do unless an IN packet is waiting and the ring has data = */ + if (!p || cur_rptr =3D=3D new_wptr) { + return; + } + + switch (aspeed_udc_ep_xfer_in(s, ep, p)) { + case ASPEED_UDC_XFER_DONE: + e->pkt =3D NULL; + p->status =3D USB_RET_SUCCESS; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + break; + case ASPEED_UDC_XFER_ERROR: + e->pkt =3D NULL; + p->status =3D USB_RET_IOERROR; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + break; + case ASPEED_UDC_XFER_MORE: + break; + } +} + +/* + * OUT kick: the guest gadget driver wrote EP_DMA_STS to give us a buffer = for + * OUT data. If an OUT packet is already waiting (parked because there was= no + * buffer before), copy its data into the buffer now and finish it. If the + * packet has more data than fits, keep it parked and wait for the next bu= ffer. + */ +static void aspeed_udc_ep_out_kick(AspeedUDCState *s, int ep) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + USBPacket *p =3D e->pkt; + + /* nothing to do unless an OUT packet is waiting and a buffer is ready= */ + if (!p || !FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, WPTR)) { + return; + } + + switch (aspeed_udc_ep_xfer_out(s, ep, p)) { + case ASPEED_UDC_XFER_DONE: + e->pkt =3D NULL; + p->status =3D USB_RET_SUCCESS; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + break; + case ASPEED_UDC_XFER_ERROR: + e->pkt =3D NULL; + p->status =3D USB_RET_IOERROR; + usb_packet_complete(USB_DEVICE(s->usbgadget), p); + break; + case ASPEED_UDC_XFER_MORE: + break; + } +} + static uint64_t aspeed_udc_ep_read(void *opaque, hwaddr offset, unsigned s= ize) { AspeedUDCEP *e =3D opaque; @@ -346,10 +660,31 @@ static void aspeed_udc_ep_write(void *opaque, hwaddr = offset, uint64_t data, unsigned size) { AspeedUDCEP *e =3D opaque; + AspeedUDCState *s =3D container_of(e - e->index, AspeedUDCState, ep[0]= ); uint32_t reg =3D offset >> 2; + uint32_t val =3D data; =20 - trace_aspeed_udc_ep_write(e->index, offset, data); - e->regs[reg] =3D data; + trace_aspeed_udc_ep_write(e->index, offset, val); + + switch (reg) { + case R_EP_DMA_BUFF: + e->regs[reg] =3D val & R_EP_DMA_BUFF_BASE_ADDR_MASK; + break; + case R_EP_DMA_STS: + val &=3D 0x77ffffff; + if (FIELD_EX32(e->regs[R_EP_DMA_CTRL], EP_DMA_CTRL, DESC_OP_EN)) { + /* IN, descriptor-list mode */ + aspeed_udc_ep_in_kick(s, e->index, val); + } else { + /* OUT, single-stage mode */ + e->regs[reg] =3D val; + aspeed_udc_ep_out_kick(s, e->index); + } + break; + default: + e->regs[reg] =3D val; + break; + } } =20 static const MemoryRegionOps aspeed_udc_ep_ops =3D { @@ -375,6 +710,8 @@ static void aspeed_udc_reset_hold(Object *obj, ResetTyp= e type) memset(s->regs, 0, sizeof(s->regs)); for (i =3D 0; i < ASPEED_UDC_NUM_EP; i++) { memset(s->ep[i].regs, 0, sizeof(s->ep[i].regs)); + s->ep[i].pkt =3D NULL; + s->ep[i].desc_off =3D 0; } =20 /* Device-reset default: root, DMA and EP-pool soft-reset bits set */ @@ -468,6 +805,95 @@ static void aspeed_udc_class_init(ObjectClass *klass, = const void *data) * through the MMIO register interface above. */ =20 +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; +} + +static void aspeed_udc_ep_data_in(AspeedUDCState *s, int ep, USBPacket *p) +{ + AspeedUDCEP *e =3D &s->ep[ep]; + uint32_t rptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, RPTR); + uint32_t wptr =3D FIELD_EX32(e->regs[R_EP_DMA_STS], EP_DMA_STS, WPTR); + + if (rptr =3D=3D wptr) { + /* + * No IN data is queued yet. Save the packet and return ASYNC + * instead of NAK. A NAK would make the host retry slowly. + * aspeed_udc_ep_in_kick() serves and completes this packet later, + * once the guest gadget driver queues descriptors. + */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + return; + } + + switch (aspeed_udc_ep_xfer_in(s, ep, p)) { + case ASPEED_UDC_XFER_DONE: + p->status =3D USB_RET_SUCCESS; + break; + case ASPEED_UDC_XFER_MORE: + /* not fully sent yet: save the packet, wait for more descriptors = */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + break; + case ASPEED_UDC_XFER_ERROR: + p->status =3D USB_RET_IOERROR; + break; + } +} + +static void aspeed_udc_ep_data_out(AspeedUDCState *s, int ep, USBPacket *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, PKT_SIZE), + p->iov.size); + if (!FIELD_EX32(sts, EP_DMA_STS, WPTR)) { + /* + * No OUT buffer is ready yet. Save the packet and return ASYNC + * instead of NAK. Writing now could use an old buffer address and + * lose the data (for example a mass-storage CBW). A NAK would make + * the host retry slowly. aspeed_udc_ep_out_kick() delivers this + * packet later, once the guest gadget driver sets up a buffer. + */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + return; + } + + switch (aspeed_udc_ep_xfer_out(s, ep, p)) { + case ASPEED_UDC_XFER_DONE: + p->status =3D USB_RET_SUCCESS; + break; + case ASPEED_UDC_XFER_MORE: + /* not fully received yet: save the packet, wait for the next buff= er */ + e->pkt =3D p; + p->status =3D USB_RET_ASYNC; + break; + case ASPEED_UDC_XFER_ERROR: + p->status =3D USB_RET_IOERROR; + break; + } +} + static void aspeed_udc_gadget_handle_reset(USBDevice *udev) { AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; @@ -531,17 +957,37 @@ static void aspeed_udc_gadget_handle_control(USBDevic= e *udev, USBPacket *p, =20 static void aspeed_udc_gadget_handle_data(USBDevice *udev, USBPacket *p) { - /* Programmable endpoint (bulk) transfers are added in a later patch. = */ - p->status =3D USB_RET_STALL; + 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); + } } =20 static void aspeed_udc_gadget_cancel_packet(USBDevice *udev, USBPacket *p) { AspeedUDCState *s =3D ASPEED_UDC_GADGET(udev)->udc; + int i; =20 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; + } + } } =20 static void aspeed_udc_gadget_realize(USBDevice *udev, Error **errp) diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 098c3d6179..80ead23358 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -389,3 +389,7 @@ 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" --=20 2.53.0 From nobody Sat Sep 26 22:14: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=1788315445; cv=pass; d=zohomail.com; s=zohoarc; b=NKjmNnz8qb/seDf5I1kTp9cN2EHnq4CHLBGeihlg3EGZhTUAVNZ+gHrBj/DheTcmErnkCzMt8UxUaUD/rX24HoO0ReqvzgjyFgEc0qfBaSbylqdMUvotyfaEV3RyqeA1TggWuB8yBV1fQOm6PWwTJ4hdEF2htlNFfEpVRpYkY0Q= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1788315445; 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=vqkfcQMqJHPx6pJdymHcdC4wb8lwS2Iarn1ss43vlJI=; b=WiULfNUFyXxPuuiOX7p57xokXh4j8hTZJDF0NoAUG+IvFWQFwpCEG45cYABOAG1kwa60SQJ8Y2IHmDWRXMqZCQ0Iph/A7HCHwjtuFbGjPXlrHuTLuV6l7WQ5oucItmsAPRHS8d/im4+Ru3I6pKX50bM45Bx8bWzjKHLHNAzcXCw= 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 178831544481157.39841112015131; Tue, 1 Sep 2026 19:17:24 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x1aWM-0003PT-Nx; Tue, 01 Sep 2026 22:16:14 -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 1x1aWL-0003PH-La; Tue, 01 Sep 2026 22:16:13 -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 1x1aWJ-0007bI-Tq; Tue, 01 Sep 2026 22:16:13 -0400 Received: from TYZPR06MB4980.apcprd06.prod.outlook.com (2603:1096:400:1cc::10) by TYZPR06MB5782.apcprd06.prod.outlook.com (2603:1096:400:26a::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.13; Wed, 2 Sep 2026 02:15:47 +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.0360.008; Wed, 2 Sep 2026 02:15:47 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=LqrRiwGAa6uo9LETzpf/vdGXtxJb0LheDqYICsCu1Dxch7tID65OYYlnx9BT3WX/92P6s6vayPHpe8ZlacZ9HNUunx/SxnhpivZfNzgi2JdrX/O/1H0aKpWq1m8Zq2oYHpuPElNdjwoO7P0Flbv5RaBI33ypFrGppeqWYY49x8f8+zfg8XnYwcDrg25omEUmn6Q9leeJ3G9KXgMGooGqB+VhSEE5xRFKNY6UoVf9A6gHk6LmMctzgA043whsLQFQ8xPgDIONRBjcJZS/slOj3huwm1Qn+8etzM8X3cTVxSkP2zMd2Iks9sTvltyB3788NCfnpx1OmEn58ztM9Bq0EQ== 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=vqkfcQMqJHPx6pJdymHcdC4wb8lwS2Iarn1ss43vlJI=; b=GysJ+sG5tEvjqx1S/aMNqYigHoBu84As88lJwx77dvoKJtzUjQ1pPV34nl5L6cW/CPxVfpHw1z0V2uFIBOrQ7dPxsKi+J2/xJiIu59j13cX3RZGZT5xTxBuCS27BRMXYOQYmuLULV8RNRZcNwCsUV77SvToAalR+R1cNFq1iZPJ0aWC0w+QSIroDuR9x/kE9aZpuejRdkj8byYo+OppdIG5cPZGVU+e+m2Liphag60b3wVti9Kp5qqBsGOyPXEvDkLyCG2oY4aievgQkH/q/sSI/7a0BBnAxfT8W0TfD2M5ugko0sCazwlpqK7UKfltUKr3i/6VXGQqpNEh6e8z5yw== 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=vqkfcQMqJHPx6pJdymHcdC4wb8lwS2Iarn1ss43vlJI=; b=VVlGF7RP9KcuM5dnMRzvDc6h/GdAmjsZmgnMKJ9VeHdOizhXs7oNi/uKydG6UPbityeNKV7NDkTGxjyDZWkWj8n4uVJNzLfKEcKTABwkxbt9mo4YRXg+10uLTaVLw410+C8F/h12p/KNNTQXN9uzHsVjn3B18G1xTwokY75eOB16S/AXlNXfC0UQC992mb2S7j8ATv55QWfzzZwAo4PUtuljIvvLV/tPvgUPyGsa/Igz8VuIJrb325cZ0ZAz3IHSd8hZIv3VrbElKLLdp4JWsf4nYalLVcKv95vrR1NwHes8lKH9JCv0LnBpvKhQquSNTUT2iSWZq1Utthco7g4e/A== 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 , =?iso-8859-1?Q?Philippe_Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric_Le_Goater?= Subject: [PATCH v5 4/4] hw/arm/aspeed_ast2600: Wire up the UDC Thread-Topic: [PATCH v5 4/4] hw/arm/aspeed_ast2600: Wire up the UDC Thread-Index: AQHdOoD3l+ttzKGGTUG0/bEhwn4n+A== Date: Wed, 2 Sep 2026 02:15:47 +0000 Message-ID: <20260902021542.3194812-5-jamin_lin@aspeedtech.com> References: <20260902021542.3194812-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260902021542.3194812-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_|TYZPR06MB5782:EE_ x-ms-office365-filtering-correlation-id: 7f47c92c-5d02-4f3c-4d6d-08df08981a32 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|1800799024|376014|7416014|366016|23010399003|3023799007|10067099003|5023799004|56012099006|921020|38070700021|18002099003|22082099003; x-microsoft-antispam-message-info: Z+zRP6WbmOlk15Rl/XR791P5c/4PFkWw19UomBICc4ynlkXU14xcsBvW2Ze/VDRUZVWo4mJfF0t0Rp/ecCf5ZfT4jQXKMqZDTy0z3iRiQfB6sMJJAv6RVBHTuH20pqL08uGpen6FWQIIvlkvInSdzbXhEiV/GTTKiyeWhc12QZ40PW1b63y6MFZ0krjL1UZGe/KbTPg4ngrYUJBHF2VnU6mm4PkupSrpcsRFU9odnwAMl9veTUd9Qj9m9C4pG6r3ZnuJp/HKTZojLPBIIrDR9GOFUMKgYdw5w6lslH/xW/nWta/P0rXIHrUPuTkN71ZYujWPQrVlOKjGNIP19VZOI2Xdj0DlUmtGPm49yEtuHISsSIvYzVIQbDAuSO8wii8KDLUi3u88r8c/pm+Sbw7ZtZF/U+TLkAfnDiilXSMy+QVElOMrw6cgCP7lqXK4qOZfY9u5XSiLN7mH8cGO9+Wcw+kLs0AdJXRR2MKXBX5ObJXhTj+I84e8JOLM7MhB+/5unK0sGocx68s5kYMKp8aNVylzpFzuJ0Mp6IlC3j3SpjtTF95K7yhtS4ioyn2roklqNpSRCRzL8MuV9n1AA4nyDKSWoV5p3w8fYO4IXC2o9yXUwZOm5oqgZP3Zmvf7bOdG23cDBDu6MUfQDSVYaEgC4nG2LKpioF4Q/qV0lkgjx2B+fHFPHNfro85N+dfm7jUekKBU6PqG08xtPwnrHKUwwHdyljlhXY09Urgu8yTRtiU= 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)(1800799024)(376014)(7416014)(366016)(23010399003)(3023799007)(10067099003)(5023799004)(56012099006)(921020)(38070700021)(18002099003)(22082099003); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?3+aKpJc7sOfKASjVcd/6rhpzEw5Zr9ccnhXTqf6YYPaXHmo5f+2t8X+pMj?= =?iso-8859-1?Q?FeISTwmLWgy09J7WHP8DpcEZTtXWWb/R9QhEtbfXx1w6FNmTSjXObmQWIO?= =?iso-8859-1?Q?qUZ8Myy0szzIhSHhghhnf6m9zbBhBFeIhtLbeGAE1cnw7//eWPcS3xKS+8?= =?iso-8859-1?Q?SOGb8BrR2TSHNoKYR6MUaX1kz/72HijcPXy8vCVMtjeOpHYlm/b5QBNcNO?= =?iso-8859-1?Q?9wjmoC4mjMc7VVFnBaeeSDdmCu0LwdNH9yHXmNySa20tpL+EZfU7+zgD1D?= =?iso-8859-1?Q?LnlxVbjuD6/xpt6zJc8k08w7+rRyDbC7XnRHy7Rgv+TIq618mjgn4FFIS6?= =?iso-8859-1?Q?2wnxDqqeB0nq/doLj/gaqIysk+YqKqxeQkTZ5XBTSEHqzei5j0G8UICwjq?= =?iso-8859-1?Q?fQt0GCnORw4sr7xVWMf7bLHJKGkpd+GThSRSjQubFur+n56N5+Iu2iMRB7?= =?iso-8859-1?Q?BHwEg/fRIJQ6vWAwEBgSMwgU7IxmcwkX5ITuqpU7J7qz3YST9RACNXJh2t?= =?iso-8859-1?Q?TN9RzXXRRMaQAqFRIQyxpfzIFvahkL0FlhuS9OLR8CTd5weN9tYbUfhTkY?= =?iso-8859-1?Q?VQQ8NOT1cJ9C/DPbUZ+EAoir4KmzWbUSrjn0+Lg9armiQEOBQ0gn3ix5aW?= =?iso-8859-1?Q?Uvit7IVpiRusSWywXCI/YiOWC0aZ94dETy9XPiYosGWQvZzHYIjioeHQ+h?= =?iso-8859-1?Q?/+rTx/6FECYSmxKz5C6i/P3kko6J62vS3tul8+71H+H3RTUZvnHomfSic5?= =?iso-8859-1?Q?kxJcdvia+r3sfTIQGA/EAKHq/S1ImBFOE2euw6+lRrQr4x0GrIl/pRxe0p?= =?iso-8859-1?Q?fbEpEmd4ojSWTv1CMSdjiOnvjrtLiQ3LAHhSg/KeiKKv95r6u/x5WgmuIX?= =?iso-8859-1?Q?zHUxUcwhYk5NpLXnlu+uS/cFuc85zrNW+vTbMnbKHDWF8ELkn+3Kc3gVce?= =?iso-8859-1?Q?p/5tEkRtmckqtkTxqOsfo6M8MvhdOOenhBFsddq8pefylRMrQPCClSmD3r?= =?iso-8859-1?Q?sIsUzvrYPcVlPDZN6QliPkJGYMvJ51S+FxcW4ZGQlMcmkZ76oENp6I00ug?= =?iso-8859-1?Q?0Lu4qLHb+bTh3DpnphltPGjF6gJXxv5prfL4MsllJQiusJe4HKW/XhyE4O?= =?iso-8859-1?Q?0FTuDXXXuvKiBKr3EHQTmAifTPcP58G/mVWKuIWWgO98hAowd9j3HWDoqK?= =?iso-8859-1?Q?0MglW32Oe1iaTTSHK/2owZ9KJHCuPQPmxWUeoQzq7nE2Ts5d+wiWnrIzl0?= =?iso-8859-1?Q?CQ15QbNW6VNx2vOIUlL7ZgAKzaSCYSv6W5z7jA3PmR23dTe0bCT8EZYrXK?= =?iso-8859-1?Q?ckJzQenxoq5h/tWLUOZhvfET5oOOswhylm6Pioo9MqkG42VuWYAfmzAjaw?= =?iso-8859-1?Q?v4ewoUQgK5MEI57oI9Ci1CMOgY7jNdvfn9MprRvo90dwrQmspcqpQCzmtZ?= =?iso-8859-1?Q?q8KOUHnCOlqjCAhAW+BNxDLoSC2iz56URA5ybHx2Uw3MB3IcIAc5OGna91?= =?iso-8859-1?Q?C2ADuS/XbtBEWqEMSXcCo1upRUrhs0RRXbvABzNB9kZXMB728Vz6RB+9qk?= =?iso-8859-1?Q?b/TxH71lZoPVytM5wX/x/kGXMHaXR10+RFNI/jdOaqKgiqFWPta3Q0FOrQ?= =?iso-8859-1?Q?0rG8/BebgmiK2sTGI/nxPCZ+ISQDvSJbNhCdi67Km+4EXEdzMBukcXmWiA?= =?iso-8859-1?Q?JUh7xlxgg+bjQhAczUrYpViggsb4dnUZscpex6sJfKNfDqWSxh91x2S9qd?= =?iso-8859-1?Q?cN5GjD8NzEwW2zG6eWhkPbYb71L3Vwonjv+42c6ijdI8VMsBkESQ+nMfgZ?= =?iso-8859-1?Q?RZYGSFiPYQ=3D=3D?= Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: Td1ELmmgbVRiZaJlGeQxJ7QbU91sjX/Rr7fQfZem+nR/qe0MeBOexa2iN6xz3wKNzrH2eK7Y/88YMNfEGwu72+elN1qX1/P/23dbU5vIo0BTivCuBZaz9Qk60sxMtN37YxriTld6suVgLB+41TtEktNJeGJpVsJ9zwoUXjt6LemUt2xY2vlIEKzjeCLk4ycDHjGCIA1+eHBRzYN7kaCrkYpBbRMS6WXY2usSsUDUxZD+MMGCL9J/jI15MlD82K6YfgL6Mn+b938C2qFEvcjEz40LwDk1st/gK6E3n5DKL5e6PIwVA/Qz6k7ofM9vpKrf5bGIunMqW3z2DBKizTIzQg== 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: 7f47c92c-5d02-4f3c-4d6d-08df08981a32 X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Sep 2026 02:15:47.7388 (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: DrYOMtAVufoS8R/PdEY3Otp+QCE34deWgd7x0p5prVmQRnDLeNud7Iii15g3YInOGbvFs6OH3F5jPAOfVqn6ZAaDPgyJGLL2sRR+oXfiiAQ= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR06MB5782 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: 1788315449750154100 Content-Type: text/plain; charset="utf-8" Create the USB Device Controller (UDC) at 0x1e6a2000 on the AST2600 SoC and map its registers. The UDC and the second EHCI host controller (EHCI2) share one SoC interrupt line. Route both through an OR gate whose output drives that GIC input, so either device can raise the interrupt without the other clobbering the line. 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 Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: C=C3=A9dric Le Goater --- include/hw/arm/aspeed_soc.h | 4 ++++ hw/arm/aspeed_ast2600.c | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index cd68c7f1ca..fe7b31e42c 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -36,6 +36,8 @@ #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 "hw/core/or-irq.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" #include "hw/misc/unimp.h" @@ -138,6 +140,8 @@ struct Aspeed2600SoCState { =20 A15MPPrivState a7mpcore; ARMCPU cpu[ASPEED_CPUS_NUM]; /* XXX belong to a7mpcore */ + AspeedUDCState udc; + OrIRQState ehci2_udc_orgate; }; =20 #define TYPE_ASPEED2600_SOC "aspeed2600-soc" diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index d1f18e471a..21db95f5ae 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, @@ -214,6 +215,10 @@ static void aspeed_soc_ast2600_init(Object *obj) TYPE_PLATFORM_EHCI); } =20 + object_initialize_child(obj, "udc", &a->udc, TYPE_ASPEED_UDC); + object_initialize_child(obj, "ehci2-udc-orgate", &a->ehci2_udc_orgate, + TYPE_OR_IRQ); + 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), @@ -561,6 +566,16 @@ static void aspeed_soc_ast2600_realize(DeviceState *de= v, Error **errp) aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_SPI1 += i)); } =20 + /* + * EHCI2 and the UDC share one SoC interrupt line, so OR their outputs + * together and drive that GIC input from the OR gate. + */ + object_property_set_int(OBJECT(&a->ehci2_udc_orgate), "num-lines", 2, + &error_abort); + qdev_realize(DEVICE(&a->ehci2_udc_orgate), NULL, &error_abort); + qdev_connect_gpio_out(DEVICE(&a->ehci2_udc_orgate), 0, + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_EHCI2)); + /* EHCI */ for (i =3D 0; i < sc->ehcis_num; i++) { if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { @@ -568,10 +583,26 @@ static void aspeed_soc_ast2600_realize(DeviceState *d= ev, Error **errp) } aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, - aspeed_soc_ast2600_get_irq(s, - ASPEED_DEV_EHCI1 + i= )); } + /* + * EHCI1 has its own IRQ; EHCI2 shares the UDC's IRQ, so route it thro= ugh + * the OR gate. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[0]), 0, + aspeed_soc_ast2600_get_irq(s, ASPEED_DEV_EHCI1)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[1]), 0, + qdev_get_gpio_in(DEVICE(&a->ehci2_udc_orgate), 0)); + + /* UDC - USB 2.0 Device Controller */ + object_property_set_link(OBJECT(&a->udc), "dram", OBJECT(s->dram_mr), + &error_abort); + 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, + qdev_get_gpio_in(DEVICE(&a->ehci2_udc_orgate), 1)); =20 /* SDMC - SDRAM Memory Controller */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { --=20 2.53.0