From nobody Sat Feb 7 07:11:26 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=1770348208; cv=pass; d=zohomail.com; s=zohoarc; b=KkmcI5f11GNcVeXlWVB+eU88L0GUyB/4CADXvvfL5E7Hqvmu+WgaofrVsn5GpRHnsRySRJuqJE6CHYCQsQLl2IQMF1lScej1JKeLOJ4OexZCj5K1HiXgeUJBPQCEiV5V9y4kx4sQonotm2xS8y0QFYoDlXbjQHf8reC/eaQ+x50= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1770348208; 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=coIzXWoH62VurJ9nb4WuiuDaNXuNdya6LvP915mKc7w=; b=aJkAtbv+1GTM3yho8Nvc05eV58DuNVuqlScvfDEu8l4ieGqodkbbvPo6A9I63WPkBp5jq74aeUVRf0VKCPWEfn8GgdaFPud7JXbZyJ99OGkR+q7wtaN5mps3hVWgKyMoHEHogTqqTfMApgNUADD4UOs/Qu4lksycC43CeGV3aps= 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 lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177034820818296.5411277719951; Thu, 5 Feb 2026 19:23:28 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1voCOg-0001t1-6Q; Thu, 05 Feb 2026 22:20:42 -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 1voCO9-0000Vb-4S; Thu, 05 Feb 2026 22:20:19 -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 1voCO6-0008VR-0q; Thu, 05 Feb 2026 22:20:08 -0500 Received: from TYPPR06MB8206.apcprd06.prod.outlook.com (2603:1096:405:383::19) by TY2PPF4DD1DCAD3.apcprd06.prod.outlook.com (2603:1096:408::78d) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9564.7; Fri, 6 Feb 2026 03:19:53 +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.9587.013; Fri, 6 Feb 2026 03:19:53 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=PdQZSnQiht34Vs4VE8uUKtnPBH9SbQv7Fr57m9Y6kdfaAdwtzGFPEv7bG52QiGzwYZF3ujceIhWHgy79+H8/GxggCRx9zMVOIAk+i+0ERj6PMCWXDv30jdURdBOgCWV4j9Ba48eADGCpnQ3yYQGvpWFGfoB+V8cLsng6sVbRorwVUp4d8LBP+y/0VQqNp37l+6u2gwg0y56XdUnVkMf/1obnIdqXQKlqTpkF1SnyyWo4O4AqsuG0dX4x77M6FjqF6phVCsF1LvAfM8vu8TndIj4zJhjeVM/JTQwxZFGX3hHMDQReF5NfxJgI5X5Zb6xQXQErtpPXjOhSmM7T/Z91YA== 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=coIzXWoH62VurJ9nb4WuiuDaNXuNdya6LvP915mKc7w=; b=wky6AaBzr/w3RYJkyE7M+kxOJHluQ9iOm5u/m1J3kJnXqFh9czE+C1LdLFq30jXy9j1Oxpa/6kDTaPYFGRncxzQMoTUqtPGnHkpLg6MZX7osWxTHf2PN28YrBcSKSIS+k02skMMhBpjfvKmssPIWu7vts72eqkc7hMPA1vZoRCGLGsaaZVPPOj6e63vz1tXdi3nUGsN6kTuMOKVhFRzbF8y0XE9CeiK1GtqdO2mEiINlVAr/CFbRzHTLLrGZ6Ry98f6TpiTEHY5i9zP4azVKSxIER3iKBfd1xMRnqaOig9PSBjcp6hwuycinlA+2gbtNttkqJnRBrkHxQCsq4f3MIg== 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=coIzXWoH62VurJ9nb4WuiuDaNXuNdya6LvP915mKc7w=; b=iXx3X7mTgpWJ8FBLJcVTKfKJH4C3yjq1lyh6KcGt+qWj9BleiU2nR/PCP7WYth0yTSO/zo9PWAGYygBP6icHulvW9N2bTiRrtSVsKs2Zoxuy9nOyNZnbk5bsvEl4wKRHEvrAo7IKCSc/2AlyagA6tS8N6zNhpeOvc6QtCV2F0Dbgaw8CZ/uusdENfap9fDdidAoxTtP0n2iJxA7D6QW/BHaauaMFqtxDuW3G1de1ALY1uRI4P1RtEcPvErdvf465HAilrfLL1yocxoGuULl5CBJk851nMtPtysDtMHt3uiCY0D6/W3OFNtK9WqLqtfDJZvN5fwJw1KTgZzebNl5TMA== 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 , Joe Komlodi , Titus Rwantare , Patrick Venture Subject: [PATCH v3 17/20] hw/i3c: Add Mock target Thread-Topic: [PATCH v3 17/20] hw/i3c: Add Mock target Thread-Index: AQHclxd1Ocdmn8es6kuMtBPXydcz4Q== Date: Fri, 6 Feb 2026 03:19:52 +0000 Message-ID: <20260206031926.3227848-18-jamin_lin@aspeedtech.com> References: <20260206031926.3227848-1-jamin_lin@aspeedtech.com> In-Reply-To: <20260206031926.3227848-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_|TY2PPF4DD1DCAD3:EE_ x-ms-office365-filtering-correlation-id: 22980915-d954-4bc7-e8eb-08de652e987e x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|366016|1800799024|7416014|376014|38070700021|921020; x-microsoft-antispam-message-info: =?iso-8859-1?Q?WaOHlzkf0G+QCfFqS7BQ213+1CKI5GFWYSLxVvcmgwX3/bwELBsnP8+3u8?= =?iso-8859-1?Q?egnYbjxN7yDA38rJ9nK1Dbr1dRg0F+T1EUyh1hJljZBL1+2TnbOOJIwwze?= =?iso-8859-1?Q?ufi2ElxeUS09Tu5VROnfZDgBpDM7MmYo7CxEST7XBoiK2ARBJs2bp7cndX?= =?iso-8859-1?Q?bYG2zIVw7q3B6LjCRbGw15qgSo89wwGnBcJ4Xud7y20PgmwM7w0lwtsZ9m?= =?iso-8859-1?Q?+GmqomBYFJD2+2bSfC3iYzm237t0nunVzrBgvMUG5RGv3LvskkeV/fSgKn?= =?iso-8859-1?Q?lIK2D5LHNzDYBDFghaOnYnUcc4bhmBYmRNaIrJFDRZoYn4hr0OWmjWOp42?= =?iso-8859-1?Q?Cz/q+IrHSbJtHfch6GZv8fLw7KGBdYzP5/SOHH9vf9eIwA1g3ryiuKcwBz?= =?iso-8859-1?Q?HiMX10qByO4Nal7Sm3CYvIZgNSaYqWMImEOy3eUUNgjAtaUCkdEFm5rsPE?= =?iso-8859-1?Q?EneC6187844Bei/lNBgOGI4DXYwGokloXl9FiNmCwQ2OcU22tKbpO448pg?= =?iso-8859-1?Q?Jyu/TJurCowjbZ/VQUT5DIcwJDOTfi01SQAeU7F0EPFNjyjiti3FxKPF71?= =?iso-8859-1?Q?VwHbl4bykKMQ/DI/fbQCq2+fi71vbbrWLcgxvE7B+GDVd8ZXx6rrdBb0g/?= =?iso-8859-1?Q?1L3jNM3zOme7TQUQx55bKNTfObBITzErtZMsFOpwUpZXBJXtJ0h7I6hjYb?= =?iso-8859-1?Q?gvoL6UeOXInv033qu0JI3EM6aIg0YGI2q5G+7lUsHIO4Mdi6GACfUZS9sV?= =?iso-8859-1?Q?iYskDSJkHaai5z1amLLMdtQJzaOuUoUsCKk3WWLtm/+Flt2kIOZ41wi2qT?= =?iso-8859-1?Q?B7uOMOaz2oJvKRPgwT5W6RYAnepvlq6P1HHJ77jo8x70rq5bap/UWTDZT6?= =?iso-8859-1?Q?Dpi7XZPLn9B0Jk/ucYVdLt+kDKXZzAb7jJlenyrKMcS6UyH340Jzc07czR?= =?iso-8859-1?Q?8XrB6ojhqaW+QQoBuYtSY4Pn+Vgz7HycLbVZq3TKa1/IvNVU5cGq5/1zHt?= =?iso-8859-1?Q?GGu0BBLVZ/MQHQY1+i83x4F0p+bZyxZDw3TklRBeIfbmZmSg8YD3Muu6Y4?= =?iso-8859-1?Q?A6OePc7YzZ3TFhMbsF13nIntqNAUVnYrKyVyjzRoi8DHA0lgZ2aGS8bUs1?= =?iso-8859-1?Q?7zWOVxYaP7E1f2fwzubAhyYz6WC+NGA8E93jt9m6WLzNEN9K2mrAlTajVi?= =?iso-8859-1?Q?Q0LQB4ncyGH1yVzb16qPMwIkXaxvJrrDctzkDkAMxU3kbii+XTCVSxsWV5?= =?iso-8859-1?Q?mifJ9rwDSp618SRSi7P5utoc1CDwZfXumJp0KZDHs5kYDBIFt8a8IGNsj6?= =?iso-8859-1?Q?+vRiICOmJzJ3Nlo/ttmlTOL12B3JdFCNekM9DbfzV7bykilxIUipHFMzGA?= =?iso-8859-1?Q?h/Phw9hfmSKCr+19VOjFMRs21seqhJjSn+iOn5/kXn84iQcOuXDp5BaOnG?= =?iso-8859-1?Q?tzPTwx9zDB1hhkZbg91YjgmDoYEwuVoXqm70R63CaBM+QamMEoxZvP7YLB?= =?iso-8859-1?Q?Q1V2Ad0tQ+FmgyNga6AHKhh83sMay+kl2+hBpnCIVmkOK28DmNMDfAHjqo?= =?iso-8859-1?Q?Zk5shU+/5SGhek5G92SurnTfZGOXPv16nJXM4oJpIG80qHRZ6swAOdzlfY?= =?iso-8859-1?Q?1g2i3QFcqmZn2B5mih6f+/DtO4vjC7ZSnRooiLrNqn+Dt4Z0XBzKR9fD1K?= =?iso-8859-1?Q?5PhTLounIfRrbKwDcNzQRb94BXQ5siSypWTEv2fq?= 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)(366016)(1800799024)(7416014)(376014)(38070700021)(921020); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?Q?7jtWgWTpP2oulB7/nrfFrVz9AWpGSOdBH4bG2qKIPGApkRTfXeumcA8AX5?= =?iso-8859-1?Q?X+nCFyXs43OSzn3ASlMLX/XrmUGtmrks3IzylwBzJaWGhlAcH5cw564Soq?= =?iso-8859-1?Q?EF3Zr40V9J6JsaiaIfubb1S8lYzdiniHtvKrH9lMkO1mZquXgfDeezc3Ix?= =?iso-8859-1?Q?ARWPmM3UdWC1x4vDew/MdkAQPFfqeG2QE5RKRnjgHU+SKMn6DB5g0pwO4m?= =?iso-8859-1?Q?8AxOnXKMBFNRk+sXf3fGGzvP23IY/uyJpRUlKgrEbCrr40rvDzeVdY2HeI?= =?iso-8859-1?Q?MM/TTqL2eYFlOiZS8PhHIAuzfBL/NyWpALPMIL3Ds/gVv6ev9lA8cS+jza?= =?iso-8859-1?Q?S3iEFN8qVGuXiiefUFX4aB9MgyYFssytYkgk1h00i3TYpLCq7V+q79vq/v?= =?iso-8859-1?Q?muKbwqtL9/GL34lvehVJd4z9W43mtqefyINNfjyAVBFQnHAeRBa6guZsiw?= =?iso-8859-1?Q?CbkrSFugvo5f8goTkVBDTZXooUrp/W5GxNr8LBXhCouLK3nn6XKGoHHplr?= =?iso-8859-1?Q?AbDVC4ZqbpE5Z6n8/aYEhTU+XNiptUqaRgtoqFkpGDSMy9VKkcKTkE6rq8?= =?iso-8859-1?Q?PDmUAXKDjQ8O1AEp21f72KCoCThh/tTs8QGBuaP2wQH/rRn8tGmYlJIpAN?= =?iso-8859-1?Q?mowdn37ajmRnfnIwkMfzSs7hY3u99Ri52gXNlSMzvOn6kdpH4F1PwDqlRj?= =?iso-8859-1?Q?RrOGsAImDQFqXbqQgQ/P2GJvuh6S211r53ibzoC/hiLJoeThZAYmtmDmTI?= =?iso-8859-1?Q?D5I/nWVDFX5Z2T4aE6D9tJwKmMdf/CTq8v6f4QgxBW1vZmThzsXbbM0nTY?= =?iso-8859-1?Q?Rpz7nrfFrbni2Xb6jDE99LB6jLNBojjHftJQt8b5SmYtO0h5lcO+91nIhd?= =?iso-8859-1?Q?jbLXedKa3uUliBY9HsAdPCnLfNXiePlgQ78QBFtkDXGe+RPpOcfv6yt13C?= =?iso-8859-1?Q?Bm5MuIdmx47K6GbAdIp5cGl1iH4//R2NanAcLBtcLGWLGfADmvtwNpJWIC?= =?iso-8859-1?Q?Y2kG+oPjk5BoM38D18WiahJnnVQd+pKXPKksp/Q3U+b8GnNHvGFtfLRt0n?= =?iso-8859-1?Q?dbsUIpu//j8odfr6a6S3bnZEXU1aU4T/y9OPJUyYcN9LEHuLV/EfGtfs+R?= =?iso-8859-1?Q?et1MHxvZkyc1WRez6E7eRJprGQ48FxKDvAfAVbJEYrBR8EdonkXIen3Nce?= =?iso-8859-1?Q?TIuZ32v0FBupyZCw82+vij1YNRZ79Sl8hJXigqTk7xgQ1yJjhvGa1VNGRS?= =?iso-8859-1?Q?kW5WDYtcq0xtO+n9VRlq38BwskO4GOW1t84ePTr19G+sfqKChZ6UTvGLGL?= =?iso-8859-1?Q?gegVzpsAEOYDj3/sTOGgAmCmTTRg4B2t5vRav4FfRX6t2ZeKn4PXRrXCA8?= =?iso-8859-1?Q?eTdfrcVW09cZCF3GkNl2TbNKOvZK0TUJ4BGcAqlcwO+Vgcxho8yDllfyVM?= =?iso-8859-1?Q?d7zzmYzMU/PvS2/fmWASao+8JtiHYVbsykum908Cvn3uh+XLFJJcSNr6BN?= =?iso-8859-1?Q?S+XJm+OVpjEM8VOxxONGE8OIIbnwaYG079zhc+zVBy2SE6zfdq1diRGanD?= =?iso-8859-1?Q?LQz4bhWHoQlVH8J8VXzIsAkxMAh3/N3M1d9/wyr7NBeECBcNQje+EVY8ro?= =?iso-8859-1?Q?097JLn+EP+WfXUIvivaxx+YA/YwLE+p29+Q6cEhq4pSA2Gz3PHvMRJJZDq?= =?iso-8859-1?Q?27qQCV+jnQBiTkGRal9SvvCkVnqdBsoPdM9JhcR9GJULtKwzvfyFaud8Oy?= =?iso-8859-1?Q?rNXvNF8yHpFdufhGGjE7otioa3A3+L+Q+e0djN1WqQvzBX924X4vLb3JuP?= =?iso-8859-1?Q?fWbbGjxhnQ=3D=3D?= 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: 22980915-d954-4bc7-e8eb-08de652e987e X-MS-Exchange-CrossTenant-originalarrivaltime: 06 Feb 2026 03:19:52.0803 (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: p7RMH1+6ziURdL3WXLpNnofoCiGP0IU3g59bNX9z/9L6zqK5C/PPbFxTmrE6xQUEzEjXXG8/8zZtV3tkgGlLkEdJoe1p89p5Dsdd2U+qCes= X-MS-Exchange-Transport-CrossTenantHeadersStamped: TY2PPF4DD1DCAD3 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=lists.gnu.org; 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-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: 1770348210667154100 Content-Type: text/plain; charset="utf-8" Adds a simple i3c device to be used for testing in lieu of a real device. The mock target supports the following features: - A buffer that users can read and write to. - CCC support for commonly used CCCs when probing devices on an I3C bus. - IBI sending upon receiving a user-defined byte. Signed-off-by: Joe Komlodi Signed-off-by: Jamin Lin Reviewed-by: Titus Rwantare Reviewed-by: Patrick Venture Reviewed-by: Jamin Lin --- include/hw/i3c/mock-i3c-target.h | 52 ++++++ hw/i3c/mock-i3c-target.c | 311 +++++++++++++++++++++++++++++++ hw/i3c/Kconfig | 10 + hw/i3c/meson.build | 1 + hw/i3c/trace-events | 9 + 5 files changed, 383 insertions(+) create mode 100644 include/hw/i3c/mock-i3c-target.h create mode 100644 hw/i3c/mock-i3c-target.c diff --git a/include/hw/i3c/mock-i3c-target.h b/include/hw/i3c/mock-i3c-tar= get.h new file mode 100644 index 0000000000..7ac55a3179 --- /dev/null +++ b/include/hw/i3c/mock-i3c-target.h @@ -0,0 +1,52 @@ +#ifndef MOCK_I3C_TARGET_H_ +#define MOCK_I3C_TARGET_H_ + +/* + * Mock I3C Device + * + * Copyright (c) 2025 Google LLC + * + * The mock I3C device can be thought of as a simple EEPROM. It has a buff= er, + * and the pointer in the buffer is reset to 0 on an I3C STOP. + * To write to the buffer, issue a private write and send data. + * To read from the buffer, issue a private read. + * + * The mock target also supports sending target interrupt IBIs. + * To issue an IBI, set the 'ibi-magic-num' property to a non-zero number,= and + * send that number in a private transaction. The mock target will issue a= n IBI + * after 1 second. + * + * It also supports a handful of CCCs that are typically used when probing= I3C + * devices. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "hw/i3c/i3c.h" + +#define TYPE_MOCK_I3C_TARGET "mock-i3c-target" +OBJECT_DECLARE_SIMPLE_TYPE(MockI3cTargetState, MOCK_I3C_TARGET) + +struct MockI3cTargetState { + I3CTarget i3c; + + /* General device state */ + bool can_ibi; + QEMUTimer qtimer; + size_t p_buf; + uint8_t *buf; + + /* For Handing CCCs. */ + bool in_ccc; + I3CCCC curr_ccc; + uint8_t ccc_byte_offset; + + struct { + uint32_t buf_size; + uint8_t ibi_magic; + } cfg; +}; + +#endif diff --git a/hw/i3c/mock-i3c-target.c b/hw/i3c/mock-i3c-target.c new file mode 100644 index 0000000000..e540a6c2f7 --- /dev/null +++ b/hw/i3c/mock-i3c-target.c @@ -0,0 +1,311 @@ +/* + * Mock I3C Device + * + * Copyright (c) 2025 Google LLC + * + * The mock I3C device can be thought of as a simple EEPROM. It has a buff= er, + * and the pointer in the buffer is reset to 0 on an I3C STOP. + * To write to the buffer, issue a private write and send data. + * To read from the buffer, issue a private read. + * + * The mock target also supports sending target interrupt IBIs. + * To issue an IBI, set the 'ibi-magic-num' property to a non-zero number,= and + * send that number in a private transaction. The mock target will issue a= n IBI + * after 1 second. + * + * It also supports a handful of CCCs that are typically used when probing= I3C + * devices. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/i3c/i3c.h" +#include "hw/i3c/mock-i3c-target.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#ifndef MOCK_I3C_TARGET_DEBUG +#define MOCK_I3C_TARGET_DEBUG 0 +#endif + +#define DB_PRINTF(...) do { \ + if (MOCK_I3C_TARGET_DEBUG) { \ + qemu_log("%s: ", __func__); \ + qemu_log(__VA_ARGS__); \ + } \ + } while (0) + +#define IBI_DELAY_NS (1 * 1000 * 1000) + +static uint32_t mock_i3c_target_rx(I3CTarget *i3c, uint8_t *data, + uint32_t num_to_read) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + uint32_t i; + + /* Bounds check. */ + if (s->p_buf =3D=3D s->cfg.buf_size) { + return 0; + } + + for (i =3D 0; i < num_to_read; i++) { + data[i] =3D s->buf[s->p_buf]; + trace_mock_i3c_target_rx(data[i]); + s->p_buf++; + if (s->p_buf =3D=3D s->cfg.buf_size) { + break; + } + } + + /* Return the number of bytes we're sending to the controller. */ + return i; +} + +static void mock_i3c_target_ibi_timer_start(MockI3cTargetState *s) +{ + int64_t now =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&s->qtimer, now + IBI_DELAY_NS); +} + +static int mock_i3c_target_tx(I3CTarget *i3c, const uint8_t *data, + uint32_t num_to_send, uint32_t *num_sent) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + int ret; + uint32_t to_write; + + if (s->cfg.ibi_magic && num_to_send =3D=3D 1 && s->cfg.ibi_magic =3D= =3D *data) { + mock_i3c_target_ibi_timer_start(s); + return 0; + } + + /* Bounds check. */ + if (num_to_send + s->p_buf > s->cfg.buf_size) { + to_write =3D s->cfg.buf_size - s->p_buf; + ret =3D -1; + } else { + to_write =3D num_to_send; + ret =3D 0; + } + for (uint32_t i =3D 0; i < to_write; i++) { + trace_mock_i3c_target_tx(data[i]); + s->buf[s->p_buf] =3D data[i]; + s->p_buf++; + } + return ret; +} + +static int mock_i3c_target_event(I3CTarget *i3c, enum I3CEvent event) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + + trace_mock_i3c_target_event(event); + if (event =3D=3D I3C_STOP) { + s->in_ccc =3D false; + s->curr_ccc =3D 0; + s->ccc_byte_offset =3D 0; + s->p_buf =3D 0; + } + + return 0; +} + +static int mock_i3c_target_handle_ccc_read(I3CTarget *i3c, uint8_t *data, + uint32_t num_to_read, + uint32_t *num_read) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + + switch (s->curr_ccc) { + case I3C_CCCD_GETMXDS: + /* Default data rate for I3C. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >=3D 2) { + break; + } + data[s->ccc_byte_offset] =3D 0; + *num_read =3D s->ccc_byte_offset; + s->ccc_byte_offset++; + } + break; + case I3C_CCCD_GETCAPS: + /* Support I3C version 1.1.x, no other features. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >=3D 2) { + break; + } + if (s->ccc_byte_offset =3D=3D 0) { + data[s->ccc_byte_offset] =3D 0; + } else { + data[s->ccc_byte_offset] =3D 0x01; + } + *num_read =3D s->ccc_byte_offset; + s->ccc_byte_offset++; + } + break; + case I3C_CCCD_GETMWL: + case I3C_CCCD_GETMRL: + /* MWL/MRL is MSB first. */ + while (s->ccc_byte_offset < num_to_read) { + if (s->ccc_byte_offset >=3D 2) { + break; + } + data[s->ccc_byte_offset] =3D (s->cfg.buf_size & + (0xff00 >> (s->ccc_byte_offset * 8= ))) >> + (8 - (s->ccc_byte_offset * 8)); + s->ccc_byte_offset++; + *num_read =3D num_to_read; + } + break; + case I3C_CCC_ENTDAA: + case I3C_CCCD_GETPID: + case I3C_CCCD_GETBCR: + case I3C_CCCD_GETDCR: + /* Nothing to do. */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_c= cc); + return -1; + } + + trace_mock_i3c_target_handle_ccc_read(*num_read, num_to_read); + return 0; +} + +static int mock_i3c_target_handle_ccc_write(I3CTarget *i3c, const uint8_t = *data, + uint32_t num_to_send, + uint32_t *num_sent) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + + if (!s->curr_ccc) { + s->in_ccc =3D true; + s->curr_ccc =3D *data; + trace_mock_i3c_target_new_ccc(s->curr_ccc); + } + + *num_sent =3D 1; + switch (s->curr_ccc) { + case I3C_CCC_ENEC: + case I3C_CCCD_ENEC: + s->can_ibi =3D true; + break; + case I3C_CCC_DISEC: + case I3C_CCCD_DISEC: + s->can_ibi =3D false; + break; + case I3C_CCC_ENTDAA: + case I3C_CCC_SETAASA: + case I3C_CCC_RSTDAA: + case I3C_CCCD_SETDASA: + case I3C_CCCD_GETPID: + case I3C_CCCD_GETBCR: + case I3C_CCCD_GETDCR: + case I3C_CCCD_GETMWL: + case I3C_CCCD_GETMRL: + case I3C_CCCD_GETMXDS: + case I3C_CCCD_GETCAPS: + /* Nothing to do. */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unhandled CCC 0x%.2x\n", s->curr_c= cc); + return -1; + } + + trace_mock_i3c_target_handle_ccc_write(*num_sent, num_to_send); + return 0; +} + +static void mock_i3c_target_do_ibi(MockI3cTargetState *s) +{ + if (!s->can_ibi) { + DB_PRINTF("IBIs disabled by controller"); + return; + } + + trace_mock_i3c_target_do_ibi(s->i3c.address, true); + int nack =3D i3c_target_send_ibi(&s->i3c, s->i3c.address, /*is_recv=3D= */true); + /* Getting NACKed isn't necessarily an error, just print it out. */ + if (nack) { + DB_PRINTF("NACKed from controller when sending target interrupt.\n= "); + } + nack =3D i3c_target_ibi_finish(&s->i3c, 0x00); + if (nack) { + DB_PRINTF("NACKed from controller when finishing target interrupt.= \n"); + } +} + +static void mock_i3c_target_timer_elapsed(void *opaque) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(opaque); + timer_del(&s->qtimer); + mock_i3c_target_do_ibi(s); +} + +static void mock_i3c_target_reset(I3CTarget *i3c) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(i3c); + s->can_ibi =3D false; +} + +static void mock_i3c_target_realize(DeviceState *dev, Error **errp) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(dev); + s->buf =3D g_new0(uint8_t, s->cfg.buf_size); + mock_i3c_target_reset(&s->i3c); +} + +static void mock_i3c_target_init(Object *obj) +{ + MockI3cTargetState *s =3D MOCK_I3C_TARGET(obj); + s->can_ibi =3D false; + + /* For IBIs. */ + timer_init_ns(&s->qtimer, QEMU_CLOCK_VIRTUAL, mock_i3c_target_timer_el= apsed, + s); +} + +static const Property remote_i3c_props[] =3D { + /* The size of the internal buffer. */ + DEFINE_PROP_UINT32("buf-size", MockI3cTargetState, cfg.buf_size, 0x100= ), + /* + * If the mock target receives this number, it will issue an IBI after + * 1 second. Disabled if the IBI magic number is 0. + */ + DEFINE_PROP_UINT8("ibi-magic-num", MockI3cTargetState, cfg.ibi_magic, = 0x00), +}; + +static void mock_i3c_target_class_init(ObjectClass *klass, const void *dat= a) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + I3CTargetClass *k =3D I3C_TARGET_CLASS(klass); + + dc->realize =3D mock_i3c_target_realize; + k->event =3D mock_i3c_target_event; + k->recv =3D mock_i3c_target_rx; + k->send =3D mock_i3c_target_tx; + k->handle_ccc_read =3D mock_i3c_target_handle_ccc_read; + k->handle_ccc_write =3D mock_i3c_target_handle_ccc_write; + + device_class_set_props(dc, remote_i3c_props); +} + +static const TypeInfo mock_i3c_target_info =3D { + .name =3D TYPE_MOCK_I3C_TARGET, + .parent =3D TYPE_I3C_TARGET, + .instance_size =3D sizeof(MockI3cTargetState), + .instance_init =3D mock_i3c_target_init, + .class_init =3D mock_i3c_target_class_init, +}; + +static void mock_i3c_target_register_types(void) +{ + type_register_static(&mock_i3c_target_info); +} + +type_init(mock_i3c_target_register_types) diff --git a/hw/i3c/Kconfig b/hw/i3c/Kconfig index ecec77d6fc..d5c6d4049b 100644 --- a/hw/i3c/Kconfig +++ b/hw/i3c/Kconfig @@ -3,3 +3,13 @@ config I3C =20 config DW_I3C bool + +config I3C_DEVICES + # Device group for i3c devices which can reasonably be user-plugged to= any + # board's i3c bus. + bool + +config MOCK_I3C_TARGET + bool + select I3C + default y if I3C_DEVICES diff --git a/hw/i3c/meson.build b/hw/i3c/meson.build index 83d75e7d5c..e614b18712 100644 --- a/hw/i3c/meson.build +++ b/hw/i3c/meson.build @@ -2,4 +2,5 @@ i3c_ss =3D ss.source_set() i3c_ss.add(when: 'CONFIG_I3C', if_true: files('core.c')) i3c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i3c.c')) i3c_ss.add(when: 'CONFIG_DW_I3C', if_true: files('dw-i3c.c')) +i3c_ss.add(when: 'CONFIG_MOCK_I3C_TARGET', if_true: files('mock-i3c-target= .c')) system_ss.add_all(when: 'CONFIG_I3C', if_true: i3c_ss) diff --git a/hw/i3c/trace-events b/hw/i3c/trace-events index 39f33d9a50..3d6dd4f7dd 100644 --- a/hw/i3c/trace-events +++ b/hw/i3c/trace-events @@ -36,3 +36,12 @@ legacy_i2c_recv(uint8_t byte) "Legacy I2C recv 0x%" PRIx8 legacy_i2c_send(uint8_t byte) "Legacy I2C send 0x%" PRIx8 legacy_i2c_start_transfer(uint8_t address, bool is_recv) "Legacy I2C START= with address 0x%" PRIx8 " is_recv=3D%d" legacy_i2c_end_transfer(void) "Legacy I2C STOP" + +# mock-target.c +mock_i3c_target_rx(uint8_t byte) "I3C mock target read 0x%" PRIx8 +mock_i3c_target_tx(uint8_t byte) "I3C mock target write 0x%" PRIx8 +mock_i3c_target_event(uint8_t event) "I3C mock target event 0x%" PRIx8 +mock_i3c_target_handle_ccc_read(uint32_t num_read, uint32_t num_to_read) "= I3C mock target read %" PRId32 "/%" PRId32 " bytes" +mock_i3c_target_new_ccc(uint8_t ccc) "I3C mock target handle CCC 0x%" PRIx8 +mock_i3c_target_handle_ccc_write(uint32_t num_sent, uint32_t num_to_send) = "I3C mock target send %" PRId32 "/%" PRId32 " bytes" +mock_i3c_target_do_ibi(uint8_t address, bool is_recv) "I3C mock target IBI= with address 0x%" PRIx8 " RnW=3D%d" --=20 2.43.0