From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 33E32EFB800 for ; Tue, 24 Feb 2026 05:40:47 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vul6X-0001nK-IY; Tue, 24 Feb 2026 00:37:05 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vul63-0001bK-Dh; Tue, 24 Feb 2026 00:36:37 -0500 Received: from mail-japaneastazlp170120005.outbound.protection.outlook.com ([2a01:111:f403:c405::5] helo=TYPPR03CU001.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 1vul5z-0001H5-6y; Tue, 24 Feb 2026 00:36:34 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=MSQkUGL6r4RlLxhjttRKltcHEwu5Mj0pbyfVlekHy9ozNGjc3Nalrq717/Lhti9TYAVJ6sCqCJowj2mcMqI6vocSXEwXFPbJPwI0IOhu+i4tQl4pKCCV9GpH9zgG2vx8dljkHyl9yBzpbnNUc0Mf4hykX+nk8GorjNsh5dZUZncv0GNOPGzQkmxmixD8lXN2J0ZihpKac3Yaybnsvi1aGi2mSCOOlePjrtnHHPQzlJsExMU+SjJmJNFtlhcT69ROzmdh0YBfT0CzPdLW+DCjgEhS3gLwtlOmYE9By4rd2EUulJh9FUT5tU/QdsBkBt4tWTqm8Njwh2UB5ITRdEqo6Q== 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=L0OMdpR/vO7RYC7VDX/dtooPsPgzb7fQSIEqcMM5cxM=; b=VptuB55JP9aveDlBlvGszBNhQPCgmv8ZsqwtBhu5uQDmQns/pAS3wFq235fALNOvdyuQN+WDmSxFJutg4+b9NJUPO+PWIp/brKEOA25JaRaTpa1nGF9ihsXviWqMrBLpzmioSmjoTMH86o/OLjh2R8g/3NY8RRpxMtRXbgD47Sc3oBRifLx5Y2ovsM8d8Lt5ynBWzNmcZMGYZNOoSkqUh8T2ePP33RPMmVbi5kY+P8gYfZrdzer5t3gmHSanWmbyENElWR1ySRQRBywSEawvGtdnwd43rPqfFnPjIP6YGF/+gr6Ep/q3UgExdXEQdvZ/zsV/92VbI9ZOgKKpn3sCZA== 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=L0OMdpR/vO7RYC7VDX/dtooPsPgzb7fQSIEqcMM5cxM=; b=qwVfzl0a+VLQRrwJSY5AlSLzSj7XUCFKyLMUuq+10bM7R6gP6q39ZQjxCTH5D2uoDZJmLwxSdyEV5Ojclm+2H2rZWuLj7I22diDGLebzWGWXbDpyG3P21DvbY1u79B7poJIWLPILCG3Jwn9X7vXEWfz47hFJHxK0zML+cJevZzTYW0vCd23yRaPzQD0dj5LE3whAdonGTQglD/SOCEzHu9i4G5iCgegvVKFTQ7d9uNtX7+w9RngHbL9KkAeWfYBInIpDv9HE0fbKk3XW4T4BPtOwBul85FIR5tA1eEuGDQabQBibI3/kTg3zDSoKOGMl64KFLXI7fulysOyP1Gpv1Q== Received: from TYPPR06MB8206.apcprd06.prod.outlook.com (2603:1096:405:383::19) by TYZPR06MB5179.apcprd06.prod.outlook.com (2603:1096:400:1f8::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9632.21; Tue, 24 Feb 2026 05:36:19 +0000 Received: from TYPPR06MB8206.apcprd06.prod.outlook.com ([fe80::e659:1ead:77cb:f6d3]) by TYPPR06MB8206.apcprd06.prod.outlook.com ([fe80::e659:1ead:77cb:f6d3%3]) with mapi id 15.20.9632.017; Tue, 24 Feb 2026 05:36:19 +0000 From: Jamin Lin To: Paolo Bonzini , Peter Maydell , =?iso-8859-1?Q?C=E9dric_Le_Goater?= , Steven Lee , Troy Lee , Andrew Jeffery , Joel Stanley , =?iso-8859-1?Q?Marc-Andr=E9_Lureau?= , =?iso-8859-1?Q?Daniel_P=2E_Berrang=E9?= , =?iso-8859-1?Q?Philippe_Mathieu-Daud=E9?= , "open list:All patches CC here" , "open list:ARM TCG CPUs" CC: Jamin Lin , Troy Lee , Kane Chen , "nabihestefan@google.com" , "komlodi@google.com" , Patrick Venture , Titus Rwantare Subject: [PATCH v6 03/22] hw/i3c: Add bus support Thread-Topic: [PATCH v6 03/22] hw/i3c: Add bus support Thread-Index: AQHcpU+A0Fn9fE3uzUWQZowf1ncWsg== Date: Tue, 24 Feb 2026 05:36:19 +0000 Message-ID: <20260224053613.589102-4-jamin_lin@aspeedtech.com> References: <20260224053613.589102-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260224053613.589102-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: TYPPR06MB8206:EE_|TYZPR06MB5179:EE_ x-ms-office365-filtering-correlation-id: 64ceac9d-b066-4af2-1da3-08de7366a343 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|7416014|376014|366016|1800799024|38070700021|921020; x-microsoft-antispam-message-info: =?iso-8859-1?Q?xjtHbTQ2di3AyRf8tQvDB3paxSmZ7FGjip/tpXd33KYiiI/pom9JAkTI7P?= =?iso-8859-1?Q?lEwlVDsREISXj99AR30KZ1QGtrfFSZ/H7z1jNuqzNkWRezU3RmF671f1mE?= =?iso-8859-1?Q?bgXXhSfJmbXeQScQkbXvXuDtt6K/D0IhBZkhtX5wKOp63ZOBDgWQjgyMTw?= =?iso-8859-1?Q?M9bKJgempgErUT3wm9zFTwz3ocFxb2zTQpFsNvKzE5NFsLTrQ9SePWE0WL?= =?iso-8859-1?Q?IwaDfMU+G4OBJ3V+9VeBRN6w5k3v/hju3nghh/KUR51w1w11mfLRPnKrzC?= =?iso-8859-1?Q?+mT/7vzgx3ja0zgxCRBTWEiFsB3l5yceO/ZNjy3hPQLsrldZwbrMsDMWCf?= =?iso-8859-1?Q?iTrBccaACwgLVeEdNwM+a/30psBFhWlr0xA18JYJrQwjMGKQxqTzs9oCEl?= =?iso-8859-1?Q?Xuo9Tw3IXGsIk849CYwjbca0CYDpHEVcB76VWLUYTvdqx06u93rBTxCOiB?= =?iso-8859-1?Q?cJikX2Byx/lzzn6O8pOmQOdU9xZRm7cCJD618QB+5j0zKha6oew7IDAcl6?= =?iso-8859-1?Q?0I/mYwsJaDjnZgE3uo1E1OTFj/Xddawos24jhAU1gaupziRE6btKtnuvkb?= =?iso-8859-1?Q?gTU3pkaVz/gBjKFBNGeYm2D6YH7iT/b74MVTQCUaZnBp+Cp8sM4yDLIH8/?= =?iso-8859-1?Q?NvsKmz5h3RKNPdL5aIXOABNsPku3EWYmeIF3br0oaEBkcFHTKiArZAkE12?= =?iso-8859-1?Q?9FT4WuugDh0ulo8PaYf3hdKb7jbXBPNr2O03INxdWyyIBfsAi4cZ6517ez?= =?iso-8859-1?Q?J9xjRwzgRq3qScsI78IOaoufBsQ+sHXC1NI+F+GnbA7ZGLHxAwZCB/xisp?= =?iso-8859-1?Q?HKSNQXUv4ud53qrEGw2Qud6M+NdB84KLyLWv99fRE13ZpuUXx5NDb5gxxC?= =?iso-8859-1?Q?QGN4G1VtasuezT4pj5UaMnhakxrYNHgKiSNbuLt8//NMe4wP1f/alSVdwg?= =?iso-8859-1?Q?TQ9UORx5mqoXbeigKG54dt1g/jESuMJGOYQFVczfjRD9NXaKYKRwyurN6L?= =?iso-8859-1?Q?yKcguwrL8y0z8dAN4pp2StacToheTLbItzyhwOKZIywBVLCIS+YKX6ImSj?= =?iso-8859-1?Q?s2HvGuB/7aDVy39dotIA8HpyA7T/CTQBBskGToXD3vZerMUuR2D8lwAH9w?= =?iso-8859-1?Q?FUmDeSKwzMfU3zUTYkcQBvTSlsoKmyeGzx0j+AZ4RuPtB4i5Be78iMj0FS?= =?iso-8859-1?Q?zz1wrJeRy27F5N3t8zUNUkRHtJhs9cSO5+gcz11y7lKwKxGJ4vH4R+oOYX?= =?iso-8859-1?Q?0nyGXDWhdQM4IfwZW+yAtG039oT3sZWDRBQ+1QGF7UTLJYdpX2inrxYw/A?= =?iso-8859-1?Q?nahBSbHCG8NyXxSAYmk8oJU/r4+jMNIChiCJc4qslMt0kwgyGG4KkeK7KS?= =?iso-8859-1?Q?7sH56ZTZDbBe/aBY+z6oTEFacrACzWYBPOytYJtgR9oY/U8N1Z0Qv9kyql?= =?iso-8859-1?Q?KcO3emyCNCGOXkUPEoNAzeZ9UkTxpMDglkeL/aFM+hlPIekqq1UXoB8ueL?= =?iso-8859-1?Q?rQNBJAfCaFx2suni7aVUUMggFopBu5+Json3ov9hBNSYSpzSy+OsdM9rcV?= =?iso-8859-1?Q?xNAak6D7fgO74TNW04OoaPDj6ERrhad9utucmS/7ywxHRt8MoBnAaLFKzt?= =?iso-8859-1?Q?VYX4kOJEHklryJmLVji6JBcjMiy5F4IBIHnmZVNVV48Bh5UDHBDdULEN87?= =?iso-8859-1?Q?M4ybHb4MCC+5IpmkWLIbp497pt4eA9LCKSpcYp9O?= x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:TYPPR06MB8206.apcprd06.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(7416014)(376014)(366016)(1800799024)(38070700021)(921020); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?iQzXGUVMfC74qH1n/NyDahYG1zAAaw0pPUJVR8KHDqZPSxyxITd1ZTtbxF?= =?iso-8859-1?Q?8RT+hXeCcT0NsFcinT1Y6VcfD8jAAA+ARbP/mlxVpTfG2LjkX3RbfzeaZa?= =?iso-8859-1?Q?CSNihaE8mLiN/G+fkNI7P/Xm6sVkCA3uxoEe+ivwcjmYmZrq6r5MSXiEdW?= =?iso-8859-1?Q?1ldYHieM1H6ezsCvck5DQR6Gom3+UyzDPO2goS4Yx2dPtYmZtikdj8zkIQ?= =?iso-8859-1?Q?CABVEJlwq8+gVqRjNBJHmxnuLRuNmmVSFgTQ8hEIg89oQUGuNPW63WiRHz?= =?iso-8859-1?Q?Ae23+QKrgn323muhfjWiJSzM1ElJRRoO0jqnHwSHUjHJj1hKNDjWJ8des/?= =?iso-8859-1?Q?XpSFOHzp+rrhtY7HregL7BtsDYY3mvuthalkv/PmY1NVN6fwPYZjY8c5lm?= =?iso-8859-1?Q?NjPC/Avd5c5iNbrq6k3GCkAbtbpKDyEBNw5WziZdaiMOiSx41qCc4SL3x+?= =?iso-8859-1?Q?cbKR9KLzWj9PjQWtX8kNSRHZDvQ7LlzdmOpYQy5iRRnKtYACoMti6GGcdJ?= =?iso-8859-1?Q?GZJoTuiRPHG1vNiDEH/9p2FTHMPDV5RpXpit1VZ818jXr9P44hHaJrVzwk?= =?iso-8859-1?Q?Ln8XWZjgOQf4Sn8OsdAZ2ipt5XGYygc2h2zkIFJh/SX8dSuhlOzIw4fAdE?= =?iso-8859-1?Q?nOmjb6wBLQxI/FscEpRLGv5vnVJOxPu4DDJJzEyC0iEeGciZZwYRjjIIdH?= =?iso-8859-1?Q?SIWdC6BWV73LhHjuXlms1BrYmeF3vl/aithtBuyNQQT5KG5J7QHm83VF+R?= =?iso-8859-1?Q?eW+4wti/qWpezAEtANC0UQRHdGX+H64Co7R7GTz3x7TxTPgxwXnQsf9vrd?= =?iso-8859-1?Q?HlhJ2QdMQ6OjSHAvjHBoA2WySd/xxe4Utq0wRSIbsJeDgTxeo18BcmhIqz?= =?iso-8859-1?Q?u60fQQELhnYNyj9JX6oZUwVEYXjAtrJEGbLjpPBdcxM2qFIFd+jkSyb4D8?= =?iso-8859-1?Q?q5GuE+Zwgz9ZVXZ3qbG0I7nf4hRF2ej/zbX8+YvX3COkIlMFgpg+tBm5ZW?= =?iso-8859-1?Q?kV67FCO+d4EKVe1zBEkOTdrFOwyTz6IN+fMe+J1/9gQZoGWAoEE8mFYscM?= =?iso-8859-1?Q?X6gkVeXo5NwBFdEeqA0Wscjl/U4Pj7PkgB5Lp6htd9Zq6y3N6kiVa0qTOm?= =?iso-8859-1?Q?jHWHCTpxViTiXMobqT5/k4AFtowXfrz7Pw3kwTqGWwcz/0Gn5RGKA9HNJh?= =?iso-8859-1?Q?m6MLTqdRM6v2OgMTWNqA8+U8DXAfwHt26+l7gWS1gMVfUA4S9ZIYX5l8ke?= =?iso-8859-1?Q?Hy/ehraS8zMcFPv/kQEqQzHi5LJ8NX1g9jhnlPRi2Ucq0b2I8i6x24zWPX?= =?iso-8859-1?Q?oFmP0Q3Pv6qhRyhx0Mrg4dRSBOyl3CJ69T2sxOofwu2eZngYJ1URpvYBgR?= =?iso-8859-1?Q?qZVNNNfW6s79c2oRQdv1fZ8qtDYf67rqM6cJANz73MfEkjpRsQhct9ovcj?= =?iso-8859-1?Q?M39Dbut0kEkmtYPBQvd516PJ+DgqMZLnknXiHCFJitHZ+ZuiYOO7UZmG8+?= =?iso-8859-1?Q?9e3POsHtQKel5dEzMA5jE2CVv1tOUTF80WzGC2VB1WEFlA+OaKMLOYxHw3?= =?iso-8859-1?Q?L/98auj+/TGlF/tw1mF1SIreOjuf5gKGSRqEGWWAhat8kvJQ3Bqdyb1hv7?= =?iso-8859-1?Q?44J0dUR3Rvq34HH7pL4o3v+ylJRYdr8zJTp+s8TDHeHh2Ot+pgozt/HqYT?= =?iso-8859-1?Q?CUee2gNtU8YZryjJT4uDg+cxOK8U5WX5FjdTLNMsbK6jXH1+1p0r0TEF2Y?= =?iso-8859-1?Q?xKqYf0ttKiR1GBNsIO9oYAVqU2EuS4b69e3nUIhzrg2ZfekpQaEz3DGROY?= =?iso-8859-1?Q?okjYeJDWrg=3D=3D?= Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-OriginatorOrg: aspeedtech.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: TYPPR06MB8206.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 64ceac9d-b066-4af2-1da3-08de7366a343 X-MS-Exchange-CrossTenant-originalarrivaltime: 24 Feb 2026 05:36:19.5609 (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: Y9Z2EuQy1snjiGYLw5HHrt6O9iNLQ54VGzH6gsTdsEDufGMRHTbgKW6nU9vEhyoDqb/dBNPg3O+nxoFApMKQgpxfGe3+gWzg9SwYOjhrAxM= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR06MB5179 Received-SPF: pass client-ip=2a01:111:f403:c405::5; envelope-from=jamin_lin@aspeedtech.com; helo=TYPPR03CU001.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-arm@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-arm-bounces+qemu-arm=archiver.kernel.org@nongnu.org Sender: qemu-arm-bounces+qemu-arm=archiver.kernel.org@nongnu.org Adds an I3C bus and a target class.=0A= The bus supports:=0A= - I3C data transmission and reception=0A= - CCCs (including ENTDAA)=0A= - IBIs=0A= - legacy I2C transactions=0A= =0A= General usage of the bus is similar to I2C. Users are expected to=0A= initialize a bus via i3c_init_bus, and use the bus returned from the=0A= init function to do transactions on the bus.=0A= =0A= In order to handle IBIs, the controller provides callbacks to handle=0A= receiving an IBI from a target, receiving (optional) additional IBI=0A= bytes from a target, and handling when a target is done with its IBI.=0A= =0A= Similarly, target creation is done via i3c_target_create_simple and=0A= users use the provided I3CTarget to handle transactions.=0A= The target has functions provided that it can use to invoke an IBI and=0A= send additional bytes.=0A= =0A= Along with the send, recv, and event callbacks that are expected of an=0A= I3C target, which are similar to I2C, there is a separate callback for=0A= CCC handling.=0A= This is to help encapsulate CCC handling and keep it separate from=0A= target-specific read/write functionality.=0A= =0A= To avoid repition for required CCCs among I3C targets, there is some=0A= class-level CCC handling added. The CCC is then passed to the target in=0A= case it needs to handle it in some way.=0A= =0A= Signed-off-by: Joe Komlodi =0A= Reviewed-by: Patrick Venture =0A= Reviewed-by: Titus Rwantare =0A= Reviewed-by: Jamin Lin =0A= Signed-off-by: Jamin Lin =0A= ---=0A= include/hw/i3c/i3c.h | 277 ++++++++++++++++++=0A= hw/i3c/core.c | 647 +++++++++++++++++++++++++++++++++++++++++++=0A= hw/i3c/meson.build | 1 +=0A= hw/i3c/trace-events | 16 ++=0A= 4 files changed, 941 insertions(+)=0A= create mode 100644 include/hw/i3c/i3c.h=0A= create mode 100644 hw/i3c/core.c=0A= =0A= diff --git a/include/hw/i3c/i3c.h b/include/hw/i3c/i3c.h=0A= new file mode 100644=0A= index 0000000000..6ba90793ad=0A= --- /dev/null=0A= +++ b/include/hw/i3c/i3c.h=0A= @@ -0,0 +1,277 @@=0A= +/*=0A= + * QEMU I3C bus interface.=0A= + *=0A= + * Copyright 2025 Google LLC=0A= + *=0A= + * SPDX-License-Identifier: GPL-2.0-or-later=0A= + */=0A= +=0A= +#ifndef QEMU_INCLUDE_HW_I3C_I3C_H_=0A= +#define QEMU_INCLUDE_HW_I3C_I3C_H_=0A= +=0A= +#include "hw/core/qdev.h"=0A= +#include "qom/object.h"=0A= +#include "hw/i2c/i2c.h"=0A= +=0A= +#define TYPE_I3C_TARGET "i3c-target"=0A= +OBJECT_DECLARE_TYPE(I3CTarget, I3CTargetClass, I3C_TARGET)=0A= +=0A= +typedef enum I3CEvent {=0A= + I3C_START_RECV,=0A= + I3C_START_SEND,=0A= + I3C_STOP,=0A= + I3C_NACK,=0A= +} I3CEvent;=0A= +=0A= +typedef enum I3CCCC {=0A= + /* Broadcast CCCs */=0A= + I3C_CCC_ENEC =3D 0x00,=0A= + I3C_CCC_DISEC =3D 0x01,=0A= + I3C_CCC_ENTAS0 =3D 0x02,=0A= + I3C_CCC_ENTAS1 =3D 0x03,=0A= + I3C_CCC_ENTAS2 =3D 0x04,=0A= + I3C_CCC_ENTAS3 =3D 0x05,=0A= + I3C_CCC_RSTDAA =3D 0x06,=0A= + I3C_CCC_ENTDAA =3D 0x07,=0A= + I3C_CCC_DEFTGTS =3D 0x08,=0A= + I3C_CCC_SETMWL =3D 0x09,=0A= + I3C_CCC_SETMRL =3D 0x0a,=0A= + I3C_CCC_ENTTM =3D 0x0b,=0A= + I3C_CCC_SETBUSCON =3D 0x0c,=0A= + I3C_CCC_ENDXFER =3D 0x12,=0A= + I3C_CCC_ENTHDR0 =3D 0x20,=0A= + I3C_CCC_ENTHDR1 =3D 0x21,=0A= + I3C_CCC_ENTHDR2 =3D 0x22,=0A= + I3C_CCC_ENTHDR3 =3D 0x23,=0A= + I3C_CCC_ENTHDR4 =3D 0x24,=0A= + I3C_CCC_ENTHDR5 =3D 0x25,=0A= + I3C_CCC_ENTHDR6 =3D 0x26,=0A= + I3C_CCC_ENTHDR7 =3D 0x27,=0A= + I3C_CCC_SETXTIME =3D 0x28,=0A= + I3C_CCC_SETAASA =3D 0x29,=0A= + I3C_CCC_RSTACT =3D 0x2a,=0A= + I3C_CCC_DEFGRPA =3D 0x2b,=0A= + I3C_CCC_RSTGRPA =3D 0x2c,=0A= + I3C_CCC_MLANE =3D 0x2d,=0A= + /* Direct CCCs */=0A= + I3C_CCCD_ENEC =3D 0x80,=0A= + I3C_CCCD_DISEC =3D 0x81,=0A= + I3C_CCCD_ENTAS0 =3D 0x82,=0A= + I3C_CCCD_ENTAS1 =3D 0x83,=0A= + I3C_CCCD_ENTAS2 =3D 0x84,=0A= + I3C_CCCD_ENTAS3 =3D 0x85,=0A= + I3C_CCCD_SETDASA =3D 0x87,=0A= + I3C_CCCD_SETNEWDA =3D 0x88,=0A= + I3C_CCCD_SETMWL =3D 0x89,=0A= + I3C_CCCD_SETMRL =3D 0x8a,=0A= + I3C_CCCD_GETMWL =3D 0x8b,=0A= + I3C_CCCD_GETMRL =3D 0x8c,=0A= + I3C_CCCD_GETPID =3D 0x8d,=0A= + I3C_CCCD_GETBCR =3D 0x8e,=0A= + I3C_CCCD_GETDCR =3D 0x8f,=0A= + I3C_CCCD_GETSTATUS =3D 0x90,=0A= + I3C_CCCD_GETACCCR =3D 0x91,=0A= + I3C_CCCD_ENDXFER =3D 0x92,=0A= + I3C_CCCD_SETBRGTGT =3D 0x93,=0A= + I3C_CCCD_GETMXDS =3D 0x94,=0A= + I3C_CCCD_GETCAPS =3D 0x95,=0A= + I3C_CCCD_SETROUTE =3D 0x96,=0A= + I3C_CCCD_SETXTIME =3D 0x98,=0A= + I3C_CCCD_GETXTIME =3D 0x99,=0A= + I3C_CCCD_RSTACT =3D 0x9a,=0A= + I3C_CCCD_SETGRPA =3D 0x9b,=0A= + I3C_CCCD_RSTGRPA =3D 0x9c,=0A= + I3C_CCCD_MLANE =3D 0x9d,=0A= +} I3CCCC;=0A= +=0A= +#define CCC_IS_DIRECT(_ccc) (_ccc & 0x80)=0A= +=0A= +#define I3C_BROADCAST 0x7e=0A= +#define I3C_HJ_ADDR 0x02=0A= +#define I3C_ENTDAA_SIZE 8=0A= +=0A= +struct I3CTargetClass {=0A= + DeviceClass parent_class;=0A= +=0A= + /*=0A= + * Controller to target. Returns 0 for success, non-zero for NAK or ot= her=0A= + * error.=0A= + */=0A= + int (*send)(I3CTarget *s, const uint8_t *data, uint32_t num_to_send,= =0A= + uint32_t *num_sent);=0A= + /*=0A= + * Target to controller. I3C targets are able to terminate reads early= , so=0A= + * this returns the number of bytes read from the target.=0A= + */=0A= + uint32_t (*recv)(I3CTarget *s, uint8_t *data, uint32_t num_to_read);= =0A= + /* Notify the target of a bus state change. */=0A= + int (*event)(I3CTarget *s, enum I3CEvent event);=0A= + /*=0A= + * Handle a read CCC transmitted from a controller.=0A= + * CCCs are I3C commands that I3C targets support.=0A= + * The target can NACK the CCC if it does not support it.=0A= + */=0A= + int (*handle_ccc_read)(I3CTarget *s, uint8_t *data, uint32_t num_to_re= ad,=0A= + uint32_t *num_read);=0A= + /*=0A= + * Handle a write CCC transmitted from a controller.=0A= + * CCCs are I3C commands that I3C targets support.=0A= + * The target can NACK the CCC if it does not support it.=0A= + */=0A= + int (*handle_ccc_write)(I3CTarget *s, const uint8_t *data,=0A= + uint32_t num_to_send, uint32_t *num_sent);=0A= +=0A= + /*=0A= + * Matches and adds the candidate if the address matches the candidate= 's=0A= + * address.=0A= + * Returns true if the address matched, or if this was a broadcast, an= d=0A= + * updates the device list. Otherwise returns false.=0A= + */=0A= + bool (*target_match)(I3CTarget *candidate, uint8_t address, bool is_re= ad,=0A= + bool broadcast, bool in_entdaa);=0A= +};=0A= +=0A= +struct I3CTarget {=0A= + DeviceState parent_obj;=0A= +=0A= + uint8_t address;=0A= + uint8_t static_address;=0A= + uint8_t dcr;=0A= + uint8_t bcr;=0A= + uint64_t pid;=0A= +=0A= + /* CCC State tracking. */=0A= + I3CCCC curr_ccc;=0A= + uint8_t ccc_byte_offset;=0A= + bool in_ccc;=0A= + bool in_test_mode;=0A= +};=0A= +=0A= +struct I3CNode {=0A= + I3CTarget *target;=0A= + QLIST_ENTRY(I3CNode) next;=0A= +};=0A= +=0A= +typedef struct I3CNode I3CNode;=0A= +=0A= +typedef QLIST_HEAD(I3CNodeList, I3CNode) I3CNodeList;=0A= +=0A= +#define TYPE_I3C_BUS "i3c-bus"=0A= +OBJECT_DECLARE_TYPE(I3CBus, I3CBusClass, I3C_BUS)=0A= +=0A= +struct I3CBus {=0A= + BusState parent_obj;=0A= +=0A= + /* Legacy I2C. */=0A= + I2CBus *i2c_bus;=0A= +=0A= + I3CNodeList current_devs;=0A= + bool broadcast;=0A= + uint8_t ccc;=0A= + bool in_ccc;=0A= + bool in_entdaa;=0A= + uint8_t saved_address;=0A= +};=0A= +=0A= +struct I3CBusClass {=0A= + BusClass parent_class;=0A= +=0A= + /* Handle an incoming IBI request from a target */=0A= + int (*ibi_handle) (I3CBus *bus, uint8_t addr, bool is_recv);=0A= + /* Receive data from an IBI request */=0A= + int (*ibi_recv) (I3CBus *bus, uint8_t data);=0A= + /* Do anything that needs to be done, since the IBI is finished. */=0A= + int (*ibi_finish) (I3CBus *bus);=0A= +};=0A= +=0A= +I3CBus *i3c_init_bus(DeviceState *parent, const char *name);=0A= +I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent,=0A= + const char *name);=0A= +void i3c_set_target_address(I3CTarget *dev, uint8_t address);=0A= +bool i3c_bus_busy(I3CBus *bus);=0A= +=0A= +/*=0A= + * Start a transfer on an I3C bus.=0A= + * If is_recv is known at compile-time (i.e. a device will always be sendi= ng or=0A= + * will always be receiving at a certain point), prefer to use i3c_start_r= ecv or=0A= + * i3c_start_send instead.=0A= + *=0A= + * Returns 0 on success, non-zero on an error.=0A= + */=0A= +int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv);=0A= +=0A= +/*=0A= + * Start a receive transfer on an I3C bus.=0A= + *=0A= + * Returns 0 on success, non-zero on an error=0A= + */=0A= +int i3c_start_recv(I3CBus *bus, uint8_t address);=0A= +=0A= +/*=0A= + * Start a send transfer on an I3C bus.=0A= + *=0A= + * Returns 0 on success, non-zero on an error=0A= + */=0A= +int i3c_start_send(I3CBus *bus, uint8_t address);=0A= +=0A= +void i3c_end_transfer(I3CBus *bus);=0A= +void i3c_nack(I3CBus *bus);=0A= +int i3c_send_byte(I3CBus *bus, uint8_t data);=0A= +int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send,=0A= + uint32_t *num_sent);=0A= +/*=0A= + * I3C receives can only NACK on a CCC. The target should NACK a CCC it do= es not=0A= + * support.=0A= + */=0A= +int i3c_recv_byte(I3CBus *bus, uint8_t *data);=0A= +int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read,=0A= + uint32_t *num_read);=0A= +bool i3c_scan_bus(I3CBus *bus, uint8_t address, enum I3CEvent event);=0A= +int i3c_do_entdaa(I3CBus *bus, uint8_t address, uint64_t *pid, uint8_t *bc= r,=0A= + uint8_t *dcr);=0A= +int i3c_start_device_transfer(I3CTarget *dev, int send_length);=0A= +bool i3c_target_match_and_add(I3CBus *bus, I3CTarget *target, uint8_t addr= ess,=0A= + enum I3CEvent event);=0A= +int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv);=0A= +int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data);=0A= +int i3c_target_ibi_finish(I3CTarget *t, uint8_t data);=0A= +=0A= +/*=0A= + * Legacy I2C functions.=0A= + *=0A= + * These are wrapper for I2C functions that take in an I3C bus instead of = an I2C=0A= + * bus. Internally they use the I2C bus (and devices attached to it) that'= s a=0A= + * part of the I3C bus=0A= + */=0A= +void legacy_i2c_nack(I3CBus *bus);=0A= +uint8_t legacy_i2c_recv(I3CBus *bus);=0A= +int legacy_i2c_send(I3CBus *bus, uint8_t data);=0A= +int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv);= =0A= +int legacy_i2c_start_recv(I3CBus *bus, uint8_t address);=0A= +int legacy_i2c_start_send(I3CBus *bus, uint8_t address);=0A= +void legacy_i2c_end_transfer(I3CBus *bus);=0A= +I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name,= =0A= + uint8_t addr);=0A= +=0A= +/**=0A= + * Create an I3C Target.=0A= + *=0A= + * The target returned from this function still needs to be realized.=0A= + */=0A= +I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr,=0A= + uint8_t bcr, uint64_t pid);=0A= +=0A= +/**=0A= + * Create and realize an I3C target.=0A= + *=0A= + * Create the target, initialize it, put it on the specified I3C bus, and= =0A= + * realize it.=0A= + */=0A= +I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name,=0A= + uint8_t addr, uint8_t dcr, uint8_t bcr= ,=0A= + uint64_t pid);=0A= +=0A= +/* Realize and drop the reference count on an I3C target. */=0A= +bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **err= p);=0A= +=0A= +#endif /* QEMU_INCLUDE_HW_I3C_I3C_H_ */=0A= diff --git a/hw/i3c/core.c b/hw/i3c/core.c=0A= new file mode 100644=0A= index 0000000000..22e6e226a7=0A= --- /dev/null=0A= +++ b/hw/i3c/core.c=0A= @@ -0,0 +1,647 @@=0A= +/*=0A= + * QEMU I3C bus interface.=0A= + *=0A= + * Copyright 2025 Google LLC=0A= + *=0A= + * SPDX-License-Identifier: GPL-2.0-or-later=0A= + */=0A= +=0A= +#include "qemu/osdep.h"=0A= +#include "qemu/log.h"=0A= +#include "qapi/error.h"=0A= +#include "trace.h"=0A= +#include "hw/i3c/i3c.h"=0A= +#include "hw/core/qdev-properties.h"=0A= +=0A= +/*=0A= + * In test mode (enabled by ENTTM CCC) we're supposed to send a random PID= =0A= + * during ENTDAA, so we'll just send "QEMU".=0A= + */=0A= +#define TEST_MODE_PROVISIONED_ID 0x0000554d4551ULL=0A= +=0A= +static const Property i3c_props[] =3D {=0A= + DEFINE_PROP_UINT8("static-address", struct I3CTarget, static_address, = 0),=0A= + DEFINE_PROP_UINT8("dcr", struct I3CTarget, dcr, 0),=0A= + DEFINE_PROP_UINT8("bcr", struct I3CTarget, bcr, 0),=0A= + DEFINE_PROP_UINT64("pid", struct I3CTarget, pid, 0),=0A= +};=0A= +=0A= +I3CBus *i3c_init_bus(DeviceState *parent, const char *name)=0A= +{=0A= + return i3c_init_bus_type(TYPE_I3C_BUS, parent, name);=0A= +}=0A= +=0A= +I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent,=0A= + const char *name)=0A= +{=0A= + I3CBus *bus;=0A= +=0A= + bus =3D I3C_BUS(qbus_new(type, parent, name));=0A= + QLIST_INIT(&bus->current_devs);=0A= + bus->broadcast =3D false;=0A= + bus->in_entdaa =3D false;=0A= + bus->in_ccc =3D false;=0A= +=0A= + /* I2C init. */=0A= + g_autofree gchar *i2c_bus_name =3D g_strdup_printf("%s-legacy-i2c", na= me);=0A= + bus->i2c_bus =3D i2c_init_bus(parent, i2c_bus_name);=0A= +=0A= + return bus;=0A= +}=0A= +=0A= +bool i3c_bus_busy(I3CBus *bus)=0A= +{=0A= + return !QLIST_EMPTY(&bus->current_devs);=0A= +}=0A= +=0A= +static bool i3c_target_match(I3CTarget *candidate, uint8_t address,=0A= + bool is_recv, bool broadcast, bool in_entdaa)= =0A= +{=0A= + /* Once a target has a dynamic address, it only responds to that. */= =0A= + uint8_t targ_addr =3D candidate->address ? candidate->address :=0A= + candidate->static_address;=0A= +=0A= + if (in_entdaa) {=0A= + if (address !=3D I3C_BROADCAST) {=0A= + g_autofree char *path =3D=0A= + object_get_canonical_path(OBJECT(candidate));=0A= + qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C Address 0x%.2x sent du= ring "=0A= + "ENTDAA instead of a broadcast address\n",=0A= + path, address);=0A= + return false;=0A= + }=0A= +=0A= + /*=0A= + * Targets should only ACK ENTDAA broadcasts if they have no dynam= ic=0A= + * address.=0A= + */=0A= + return candidate->address =3D=3D 0;=0A= + }=0A= +=0A= + /* Return if our addresses match, or if it's a broadcast. */=0A= + return targ_addr =3D=3D address || broadcast;=0A= +}=0A= +=0A= +bool i3c_target_match_and_add(I3CBus *bus, I3CTarget *target, uint8_t addr= ess,=0A= + enum I3CEvent event)=0A= +{=0A= + I3CTargetClass *tc =3D I3C_TARGET_GET_CLASS(target);=0A= + bool matched =3D tc->target_match(target, address, event =3D=3D I3C_ST= ART_RECV,=0A= + bus->broadcast, bus->in_entdaa);=0A= +=0A= + if (matched) {=0A= + I3CNode *node =3D g_new(struct I3CNode, 1);=0A= + node->target =3D target;=0A= + QLIST_INSERT_HEAD(&bus->current_devs, node, next);=0A= + }=0A= + return matched;=0A= +}=0A= +=0A= +bool i3c_scan_bus(I3CBus *bus, uint8_t address, enum I3CEvent event)=0A= +{=0A= + BusChild *child;=0A= + I3CNode *node, *next;=0A= +=0A= + /* Clear out any devices from a previous (re-)START. */=0A= + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {=0A= + QLIST_REMOVE(node, next);=0A= + g_free(node);=0A= + }=0A= +=0A= + QTAILQ_FOREACH(child, &bus->parent_obj.children, sibling) {=0A= + DeviceState *qdev =3D child->child;=0A= + I3CTarget *target =3D I3C_TARGET(qdev);=0A= +=0A= + if (i3c_target_match_and_add(bus, target, address, event)) {=0A= + return true;=0A= + }=0A= + }=0A= +=0A= + /* No one on the bus could respond. */=0A= + return false;=0A= +}=0A= +=0A= +/* Class-level event handling, since we do some CCCs at the class level. *= /=0A= +static int i3c_target_event(I3CTarget *t, enum I3CEvent event)=0A= +{=0A= + I3CTargetClass *tc =3D I3C_TARGET_GET_CLASS(t);=0A= + trace_i3c_target_event(t->address, event);=0A= +=0A= + if (event =3D=3D I3C_STOP) {=0A= + t->curr_ccc =3D 0;=0A= + t->ccc_byte_offset =3D 0;=0A= + t->in_ccc =3D false;=0A= + }=0A= + return tc->event(t, event);=0A= +}=0A= +=0A= +/*=0A= + * Sends a START or repeated START and the address for an I3C transaction.= =0A= + *=0A= + * This function returns 0 if a device on the bus was able to respond to t= he=0A= + * address, and non-zero otherwise.=0A= + * A non-zero return represents a NACK.=0A= + */=0A= +static int i3c_do_start_transfer(I3CBus *bus, uint8_t address,=0A= + enum I3CEvent event)=0A= +{=0A= + I3CTargetClass *tc;=0A= + I3CNode *node;=0A= +=0A= + if (address =3D=3D I3C_BROADCAST) {=0A= + bus->broadcast =3D true;=0A= + /* If we're not in ENTDAA, a broadcast is the start of a new CCC. = */=0A= + if (!bus->in_entdaa) {=0A= + bus->in_ccc =3D false;=0A= + }=0A= + } else {=0A= + bus->broadcast =3D false;=0A= + }=0A= +=0A= + /* No one responded to the address, NACK it. */=0A= + if (!i3c_scan_bus(bus, address, event)) {=0A= + return -1;=0A= + }=0A= +=0A= + QLIST_FOREACH(node, &bus->current_devs, next) {=0A= + I3CTarget *t =3D node->target;=0A= +=0A= + tc =3D I3C_TARGET_GET_CLASS(t);=0A= + if (tc->event) {=0A= + int rv =3D i3c_target_event(t, event);=0A= + if (rv && !bus->broadcast) {=0A= + return rv;=0A= + }=0A= + }=0A= + }=0A= +=0A= + return 0;=0A= +}=0A= +=0A= +int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv)=0A= +{=0A= + trace_i3c_start_transfer(address, is_recv);=0A= + return i3c_do_start_transfer(bus, address, is_recv=0A= + ? I3C_START_RECV=0A= + : I3C_START_SEND);=0A= +}=0A= +=0A= +int i3c_start_recv(I3CBus *bus, uint8_t address)=0A= +{=0A= + trace_i3c_start_transfer(address, true);=0A= + return i3c_do_start_transfer(bus, address, I3C_START_RECV);=0A= +}=0A= +=0A= +int i3c_start_send(I3CBus *bus, uint8_t address)=0A= +{=0A= + trace_i3c_start_transfer(address, false);=0A= + return i3c_do_start_transfer(bus, address, I3C_START_SEND);=0A= +}=0A= +=0A= +void i3c_end_transfer(I3CBus *bus)=0A= +{=0A= + I3CTargetClass *tc;=0A= + I3CNode *node, *next;=0A= +=0A= + trace_i3c_end_transfer();=0A= +=0A= + /*=0A= + * If we're in ENTDAA, we need to notify all devices when ENTDAA is do= ne.=0A= + * This is because everyone initially participates due to the broadcas= t,=0A= + * but gradually drops out as they get assigned addresses.=0A= + * Since the current_devs list only stores who's currently participati= ng,=0A= + * and not everyone who previously participated, we send the STOP to a= ll=0A= + * children.=0A= + */=0A= + if (bus->in_entdaa) {=0A= + BusChild *child;=0A= +=0A= + QTAILQ_FOREACH(child, &bus->parent_obj.children, sibling) {=0A= + DeviceState *qdev =3D child->child;=0A= + I3CTarget *t =3D I3C_TARGET(qdev);=0A= + tc =3D I3C_TARGET_GET_CLASS(t);=0A= + if (tc->event) {=0A= + i3c_target_event(t, I3C_STOP);=0A= + }=0A= + }=0A= + } else {=0A= + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {=0A= + I3CTarget *t =3D node->target;=0A= + tc =3D I3C_TARGET_GET_CLASS(t);=0A= + if (tc->event) {=0A= + i3c_target_event(t, I3C_STOP);=0A= + }=0A= + QLIST_REMOVE(node, next);=0A= + g_free(node);=0A= + }=0A= + }=0A= + bus->broadcast =3D false;=0A= + bus->in_entdaa =3D false;=0A= + bus->in_ccc =3D false;=0A= +}=0A= +=0A= +/*=0A= + * Any CCCs that are universal across all I3C devices should be handled he= re.=0A= + * Once they're handled, we pass the CCC up to the I3C target to do anythi= ng=0A= + * else it may want with the bytes.=0A= + */=0A= +static int i3c_target_handle_ccc_write(I3CTarget *t, const uint8_t *data,= =0A= + uint32_t num_to_send, uint32_t *num= _sent)=0A= +{=0A= + I3CTargetClass *tc =3D I3C_TARGET_GET_CLASS(t);=0A= + *num_sent =3D 0;=0A= +=0A= + /* Is this the start of a new CCC? */=0A= + if (!t->in_ccc) {=0A= + t->curr_ccc =3D *data;=0A= + t->in_ccc =3D true;=0A= + *num_sent =3D 1;=0A= + trace_i3c_target_handle_ccc(t->address, t->curr_ccc);=0A= + }=0A= +=0A= + switch (t->curr_ccc) {=0A= + case I3C_CCC_ENTDAA:=0A= + /*=0A= + * This is the last byte of ENTDAA, the controller is assigning us= an=0A= + * address.=0A= + */=0A= + if (t->ccc_byte_offset =3D=3D 8) {=0A= + t->address =3D *data;=0A= + t->in_ccc =3D false;=0A= + t->curr_ccc =3D 0;=0A= + t->ccc_byte_offset =3D 0;=0A= + *num_sent =3D 1;=0A= + }=0A= + break;=0A= + case I3C_CCCD_SETDASA:=0A= + t->address =3D t->static_address;=0A= + break;=0A= + case I3C_CCC_SETAASA:=0A= + t->address =3D t->static_address;=0A= + break;=0A= + case I3C_CCC_RSTDAA:=0A= + t->address =3D 0;=0A= + break;=0A= + case I3C_CCCD_SETNEWDA:=0A= + /* If this isn't the CCC byte, it's our new address. */=0A= + if (*num_sent =3D=3D 0) {=0A= + t->address =3D *data;=0A= + *num_sent =3D 1;=0A= + }=0A= + break;=0A= + case I3C_CCC_ENTTM:=0A= + /*=0A= + * If there are still more to look at, the next byte is the test m= ode=0A= + * byte.=0A= + */=0A= + if (*num_sent !=3D num_to_send) {=0A= + /* Enter test mode if the byte is non-zero. Otherwise exit. */= =0A= + t->in_test_mode =3D !!data[*num_sent];=0A= + ++*num_sent;=0A= + }=0A= + break;=0A= + /* Ignore other CCCs it's better to handle on a device-by-device basis= . */=0A= + default:=0A= + break;=0A= + }=0A= + return tc->handle_ccc_write(t, data, num_to_send, num_sent);=0A= +}=0A= +=0A= +int i3c_send_byte(I3CBus *bus, uint8_t data)=0A= +{=0A= + /*=0A= + * Ignored, the caller can determine how many were sent based on if th= is was=0A= + * ACKed/NACKed.=0A= + */=0A= + uint32_t num_sent;=0A= + return i3c_send(bus, &data, 1, &num_sent);=0A= +}=0A= +=0A= +int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send,=0A= + uint32_t *num_sent)=0A= +{=0A= + I3CTargetClass *tc;=0A= + I3CTarget *t;=0A= + I3CNode *node;=0A= + int ret =3D 0;=0A= +=0A= + /* If this message is a broadcast and no CCC has been found, grab it. = */=0A= + if (bus->broadcast && !bus->in_ccc) {=0A= + bus->ccc =3D *data;=0A= + bus->in_ccc =3D true;=0A= + /*=0A= + * We need to keep track if we're currently in ENTDAA.=0A= + * On any other CCC, the CCC is over on a RESTART or STOP, but ENT= DAA=0A= + * is only over on a STOP.=0A= + */=0A= + if (bus->ccc =3D=3D I3C_CCC_ENTDAA) {=0A= + bus->in_entdaa =3D true;=0A= + }=0A= + }=0A= +=0A= + QLIST_FOREACH(node, &bus->current_devs, next) {=0A= + t =3D node->target;=0A= + tc =3D I3C_TARGET_GET_CLASS(t);=0A= + if (bus->in_ccc) {=0A= + if (!tc->handle_ccc_write) {=0A= + ret =3D -1;=0A= + continue;=0A= + }=0A= + ret =3D i3c_target_handle_ccc_write(t, data, num_to_send, num_= sent);=0A= + /* Targets should only NACK on a direct CCC. */=0A= + if (ret && !CCC_IS_DIRECT(bus->ccc)) {=0A= + ret =3D 0;=0A= + }=0A= + } else {=0A= + if (tc->send) {=0A= + ret =3D ret || tc->send(t, data, num_to_send, num_sent);= =0A= + } else {=0A= + ret =3D -1;=0A= + }=0A= + }=0A= + }=0A= +=0A= + trace_i3c_send(*num_sent, num_to_send, ret =3D=3D 0);=0A= +=0A= + return ret ? -1 : 0;=0A= +}=0A= +=0A= +static int i3c_target_handle_ccc_read(I3CTarget *t, uint8_t *data,=0A= + uint32_t num_to_read, uint32_t *num_= read)=0A= +{=0A= + I3CTargetClass *tc =3D I3C_TARGET_GET_CLASS(t);=0A= + uint8_t read_count =3D 0;=0A= + uint64_t pid;=0A= +=0A= + switch (t->curr_ccc) {=0A= + case I3C_CCC_ENTDAA:=0A= + if (t->in_test_mode) {=0A= + pid =3D TEST_MODE_PROVISIONED_ID;=0A= + } else {=0A= + pid =3D t->pid;=0A= + }=0A= + /* Return the 6-byte PID, followed by BCR then DCR. */=0A= + while (t->ccc_byte_offset < 6) {=0A= + if (read_count >=3D num_to_read) {=0A= + break;=0A= + }=0A= + data[read_count] =3D (pid >> (t->ccc_byte_offset * 8)) & 0xff;= =0A= + t->ccc_byte_offset++;=0A= + read_count++;=0A= + }=0A= + if (read_count < num_to_read) {=0A= + data[read_count] =3D t->bcr;=0A= + t->ccc_byte_offset++;=0A= + read_count++;=0A= + }=0A= + if (read_count < num_to_read) {=0A= + data[read_count] =3D t->dcr;=0A= + t->ccc_byte_offset++;=0A= + read_count++;=0A= + }=0A= + *num_read =3D read_count;=0A= + break;=0A= + case I3C_CCCD_GETPID:=0A= + while (t->ccc_byte_offset < 6) {=0A= + if (read_count >=3D num_to_read) {=0A= + break;=0A= + }=0A= + data[read_count] =3D (t->pid >> (t->ccc_byte_offset * 8)) & 0x= ff;=0A= + t->ccc_byte_offset++;=0A= + read_count++;=0A= + }=0A= + *num_read =3D read_count;=0A= + break;=0A= + case I3C_CCCD_GETBCR:=0A= + *data =3D t->bcr;=0A= + *num_read =3D 1;=0A= + break;=0A= + case I3C_CCCD_GETDCR:=0A= + *data =3D t->dcr;=0A= + *num_read =3D 1;=0A= + break;=0A= + default:=0A= + /* Unhandled on the I3CTarget class level. */=0A= + break;=0A= + }=0A= +=0A= + return tc->handle_ccc_read(t, data, num_to_read, num_read);=0A= +}=0A= +=0A= +int i3c_recv_byte(I3CBus *bus, uint8_t *data)=0A= +{=0A= + /*=0A= + * Ignored, the caller can determine how many bytes were read based o= n if=0A= + * this is ACKed/NACKed.=0A= + */=0A= + uint32_t num_read;=0A= + return i3c_recv(bus, data, 1, &num_read);=0A= +}=0A= +=0A= +int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read,=0A= + uint32_t *num_read)=0A= +{=0A= + int ret =3D 0;=0A= + I3CTargetClass *tc;=0A= + I3CTarget *t;=0A= +=0A= + *data =3D 0xff;=0A= + if (!QLIST_EMPTY(&bus->current_devs)) {=0A= + tc =3D I3C_TARGET_GET_CLASS(QLIST_FIRST(&bus->current_devs)->targe= t);=0A= + t =3D QLIST_FIRST(&bus->current_devs)->target;=0A= + if (bus->in_ccc) {=0A= + if (!tc->handle_ccc_read) {=0A= + return -1;=0A= + }=0A= + ret =3D i3c_target_handle_ccc_read(t, data, num_to_read, num_r= ead);=0A= + } else {=0A= + if (tc->recv) {=0A= + /*=0A= + * Targets cannot NACK on a direct transfer, so the data= =0A= + * is returned directly.=0A= + */=0A= + *num_read =3D tc->recv(t, data, num_to_read);=0A= + }=0A= + }=0A= + }=0A= +=0A= + trace_i3c_recv(*num_read, num_to_read, ret =3D=3D 0);=0A= +=0A= + return ret;=0A= +}=0A= +=0A= +void i3c_nack(I3CBus *bus)=0A= +{=0A= + I3CTargetClass *tc;=0A= + I3CNode *node;=0A= +=0A= + if (QLIST_EMPTY(&bus->current_devs)) {=0A= + return;=0A= + }=0A= +=0A= + QLIST_FOREACH(node, &bus->current_devs, next) {=0A= + tc =3D I3C_TARGET_GET_CLASS(node->target);=0A= + if (tc->event) {=0A= + i3c_target_event(node->target, I3C_NACK);=0A= + }=0A= + }=0A= +}=0A= +=0A= +int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv)=0A= +{=0A= + I3CBus *bus =3D I3C_BUS(t->parent_obj.parent_bus);=0A= + I3CBusClass *bc =3D I3C_BUS_GET_CLASS(bus);=0A= + trace_i3c_target_send_ibi(addr, is_recv);=0A= + return bc->ibi_handle(bus, addr, is_recv);=0A= +}=0A= +=0A= +int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data)=0A= +{=0A= + I3CBus *bus =3D I3C_BUS(t->parent_obj.parent_bus);=0A= + I3CBusClass *bc =3D I3C_BUS_GET_CLASS(bus);=0A= + trace_i3c_target_send_ibi_bytes(data);=0A= + return bc->ibi_recv(bus, data);=0A= +}=0A= +=0A= +int i3c_target_ibi_finish(I3CTarget *t, uint8_t data)=0A= +{=0A= + I3CBus *bus =3D I3C_BUS(t->parent_obj.parent_bus);=0A= + I3CBusClass *bc =3D I3C_BUS_GET_CLASS(bus);=0A= + trace_i3c_target_ibi_finish();=0A= + return bc->ibi_finish(bus);=0A= +}=0A= +=0A= +static bool i3c_addr_is_rsvd(uint8_t addr)=0A= +{=0A= + const bool is_rsvd[255] =3D {=0A= + [0x00] =3D true,=0A= + [0x01] =3D true,=0A= + [0x02] =3D true,=0A= + [0x3e] =3D true,=0A= + [0x5e] =3D true,=0A= + [0x6e] =3D true,=0A= + [0x76] =3D true,=0A= + [0x7a] =3D true,=0A= + [0x7c] =3D true,=0A= + [0x7e] =3D true,=0A= + [0x7f] =3D true,=0A= + };=0A= +=0A= + return is_rsvd[addr];=0A= +}=0A= +=0A= +I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr,=0A= + uint8_t bcr, uint64_t pid)=0A= +{=0A= + DeviceState *dev;=0A= +=0A= + dev =3D qdev_new(name);=0A= + qdev_prop_set_uint8(dev, "static-address", addr);=0A= + qdev_prop_set_uint8(dev, "dcr", dcr);=0A= + qdev_prop_set_uint8(dev, "bcr", bcr);=0A= + qdev_prop_set_uint64(dev, "pid", pid);=0A= +=0A= + if (i3c_addr_is_rsvd(addr)) {=0A= + g_autofree char *path =3D object_get_canonical_path(OBJECT(dev));= =0A= + qemu_log_mask(LOG_GUEST_ERROR, "%s: I3C target created with reserv= ed "=0A= + "address 0x%.2x\n", path, addr);=0A= + }=0A= + return I3C_TARGET(dev);=0A= +}=0A= +=0A= +bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **err= p)=0A= +{=0A= + return qdev_realize_and_unref(&dev->parent_obj, &bus->parent_obj, errp= );=0A= +}=0A= +=0A= +I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, uint8_t= addr,=0A= + uint8_t dcr, uint8_t bcr, uint64_t pid= )=0A= +{=0A= + I3CTarget *dev =3D i3c_target_new(name, addr, dcr, bcr, pid);=0A= + dev->address =3D 0;=0A= + i3c_target_realize_and_unref(dev, bus, &error_abort);=0A= +=0A= + return dev;=0A= +}=0A= +=0A= +/* Legacy I2C functions. */=0A= +void legacy_i2c_nack(I3CBus *bus)=0A= +{=0A= + trace_legacy_i2c_nack();=0A= + i2c_nack(bus->i2c_bus);=0A= +}=0A= +=0A= +uint8_t legacy_i2c_recv(I3CBus *bus)=0A= +{=0A= + uint8_t byte =3D i2c_recv(bus->i2c_bus);=0A= + trace_legacy_i2c_recv(byte);=0A= + return byte;=0A= +}=0A= +=0A= +int legacy_i2c_send(I3CBus *bus, uint8_t data)=0A= +{=0A= + trace_legacy_i2c_send(data);=0A= + return i2c_send(bus->i2c_bus, data);=0A= +}=0A= +=0A= +int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv)= =0A= +{=0A= + trace_legacy_i2c_start_transfer(address, is_recv);=0A= + return i2c_start_transfer(bus->i2c_bus, address, is_recv);=0A= +}=0A= +=0A= +int legacy_i2c_start_recv(I3CBus *bus, uint8_t address)=0A= +{=0A= + trace_legacy_i2c_start_transfer(address, true);=0A= + return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=3D*/true);= =0A= +}=0A= +=0A= +int legacy_i2c_start_send(I3CBus *bus, uint8_t address)=0A= +{=0A= + trace_legacy_i2c_start_transfer(address, false);=0A= + return i2c_start_transfer(bus->i2c_bus, address, /*is_recv=3D*/false);= =0A= +}=0A= +=0A= +void legacy_i2c_end_transfer(I3CBus *bus)=0A= +{=0A= + trace_legacy_i2c_end_transfer();=0A= + i2c_end_transfer(bus->i2c_bus);=0A= +}=0A= +=0A= +I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name,= =0A= + uint8_t addr)=0A= +{=0A= + I2CSlave *dev =3D i2c_slave_new(name, addr);=0A= +=0A= + i2c_slave_realize_and_unref(dev, bus->i2c_bus, &error_abort);=0A= + return dev;=0A= +}=0A= +=0A= +static void i3c_target_class_init(ObjectClass *klass, const void *data)=0A= +{=0A= + DeviceClass *k =3D DEVICE_CLASS(klass);=0A= + I3CTargetClass *sc =3D I3C_TARGET_CLASS(klass);=0A= + set_bit(DEVICE_CATEGORY_MISC, k->categories);=0A= + k->bus_type =3D TYPE_I3C_BUS;=0A= + device_class_set_props(k, i3c_props);=0A= + sc->target_match =3D i3c_target_match;=0A= +}=0A= +=0A= +static const TypeInfo i3c_types[] =3D {=0A= + {=0A= + .name =3D TYPE_I3C_BUS,=0A= + .parent =3D TYPE_BUS,=0A= + .instance_size =3D sizeof(I3CBus),=0A= + .class_size =3D sizeof(I3CBusClass),=0A= + },=0A= + {=0A= + .name =3D TYPE_I3C_TARGET,=0A= + .parent =3D TYPE_DEVICE,=0A= + .instance_size =3D sizeof(I3CTarget),=0A= + .abstract =3D true,=0A= + .class_size =3D sizeof(I3CTargetClass),=0A= + .class_init =3D i3c_target_class_init,=0A= + },=0A= +};=0A= +=0A= +DEFINE_TYPES(i3c_types)=0A= diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build=0A= index ebf20325cb..fb127613fe 100644=0A= --- a/hw/i3c/meson.build=0A= +++ b/hw/i3c/meson.build=0A= @@ -1,3 +1,4 @@=0A= i3c_ss =3D ss.source_set()=0A= +i3c_ss.add(when: 'CONFIG_I3C', if_true: files('core.c'))=0A= i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c'))=0A= system_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss)=0A= diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events=0A= index 3ead84eb45..cdf7cb07f6 100644=0A= --- a/hw/i3c/trace-events=0A= +++ b/hw/i3c/trace-events=0A= @@ -5,3 +5,19 @@ aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read:= offset 0x%" PRIx64 "=0A= aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" P= RIx64 " data 0x%" PRIx64=0A= aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) = "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64=0A= aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data)= "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64=0A= +=0A= +# core.c=0A= +i3c_target_event(uint8_t address, uint8_t event) "I3C target 0x%" PRIx8 " = event 0x%" PRIx8=0A= +i3c_target_handle_ccc(uint8_t address, uint8_t ccc) "I3C target 0x%" PRIx8= " handling CCC 0x%" PRIx8=0A= +i3c_target_send_ibi(uint8_t address, bool is_recv) "I3C target IBI address= 0x%" PRIx8 " RnW=3D%d"=0A= +i3c_target_send_ibi_bytes(uint8_t byte) "I3C target IBI byte 0x%" PRIx8=0A= +i3c_target_ibi_finish(void) "I3C target IBI finish"=0A= +i3c_start_transfer(uint8_t address, bool is_recv) "I3C START with address = 0x%" PRIx8 " is_recv=3D%d"=0A= +i3c_end_transfer(void) "I3C transfer done"=0A= +i3c_send(uint32_t num_sent, uint32_t num_to_send, bool ack) "I3C send %" P= RId32 "/%" PRId32 " bytes, ack=3D%d"=0A= +i3c_recv(uint32_t num_read, uint32_t num_to_read, bool ack) "I3C recv %" P= RId32 "/%" PRId32 " bytes, ack=3D%d"=0A= +legacy_i2c_nack(void) "Legacy I2C NACK"=0A= +legacy_i2c_recv(uint8_t byte) "Legacy I2C recv 0x%" PRIx8=0A= +legacy_i2c_send(uint8_t byte) "Legacy I2C send 0x%" PRIx8=0A= +legacy_i2c_start_transfer(uint8_t address, bool is_recv) "Legacy I2C START= with address 0x%" PRIx8 " is_recv=3D%d"=0A= +legacy_i2c_end_transfer(void) "Legacy I2C STOP"=0A= -- =0A= 2.43.0=0A=