From nobody Mon Feb 9 01:16:43 2026 Received: from SA9PR02CU001.outbound.protection.outlook.com (mail-southcentralusazon11013068.outbound.protection.outlook.com [40.93.196.68]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5216C308F05; Thu, 29 Jan 2026 13:32:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.196.68 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769693563; cv=fail; b=p8YBso2mYbdp7ODmzmJyeXfkEgX1eaFpkttEncf5054DCwh4NSjjpXCyA6ebLhuBSaDfPY/+Cyz6mPS8M8EumbYMV0WZqjlYCv2lBD1qxD9DKZKnH5zwK2x8vgRYLSRxqxnuLOgNgYaCZVXytfmRboYGjaQKVpHXRl6l485H7YU= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769693563; c=relaxed/simple; bh=YQeamm+I9ktGWOVTJVevD60gSoea7A26viD2qLCcy6s=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=OXUPEPw+VPRKpTAzvao7nU+PtOYcXjyo3gF9F1cpNQLQ2AKQ/dJmeG2wDiYgAR6tO0ND4WT3VduovNyo/RcMGodZ8bg64spUMEVSzvo+wyVTA9W6/h+P3f4xo70oMtZGNwcGuRIZ6xQcWM3D5jWfTjFLy+8opQvpzS2oD2dsvts= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=CEUWaFpu; arc=fail smtp.client-ip=40.93.196.68 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="CEUWaFpu" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=waLjpblkpdEUQPw2OaODyKtAb3hTvrixn6sSej4rAvUVRAJZG4YcSzpeNurASvrnvJILu9RzJiF0ZXbUehst5ORDF+vmLdGemIf2mwx3/fLqeyZt3C7biidxEtJqnoicqgrskWNo8ycd5ANWUou2Oj2ECn6bSL72eC9Fqds+1ArBjV3m3gF9aml06+zejp2c9ysehNbYlL5+WBNKgYcsHf3CINEHz9bMJFcxW8pZBCebq6/LHynP8bYhCAh24dBv2s3aT7ni5sjE84bCMZgw8aMu7i4BRJYWVZ8MWhx2gR8PW74qRgHk67Kf/HwrmQ4Hnhokh/r54V+g+yqf9ww+Rg== 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=ombNWhgee0xS7DHVbt6S18r+qf/hAAFOavMOSs7QMCo=; b=vywMifpuYwmb7QGU39OBWwkTurbbWxSEmW4ImnZGybWxFwb/Ph9uW1XyQt/T4Iaz3KdI0kVLJ/awTlci1vXcHqLfJh883CcQ0UXkseiEouermagVQ8SkosdTmPKWzIlz0gPuT7dsNz2GwU4WfgXFEhTX55t+H9bkOniH4J4dk/sYFvDvsG+uxzuChxhTaxuSY4xy4vBEpmLR1554JUE9mNShlBXDjQtpymwdZniLXkGNoEBCoAjaxlktMdWFPv3mnyMYeLf4zjAE9TxDHzb0Nhh62Cpiqg+F1s1jhCrfKI/oUbdz7st1L8gXPQH2dW0tsNZuwPzDjKMIhdIoFc7VnQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ombNWhgee0xS7DHVbt6S18r+qf/hAAFOavMOSs7QMCo=; b=CEUWaFpugY4OOSGqSMN6JnSuII3gFuL3XKZwxj4Tr/kh8nOoa5dLIEpxyFXPupbH6p7/URj70ycsIR7UZMhjOFB5o5Up/Ft0d/2L4irdFn0KHzKD9feIvxk5xWdJSry6oYeJActW1zevU6H06PZQkaXzk7X+TSHPPQg6zjDwg6bSACyAw2LfPxpqj6dTwi/LuKattA6GvFh9oiZP/MpAXpQOCnXR2jfslpJB7wOsw6pM7R04MuulqrCihoApLAy/9fpuxHPKLMYHyxXCoZ/awbkHs78s7rM9FAJC6uD2hV33SZ/eMN/zcoSu04r65jKZQ1IPRmrT1jhqVl/VImm9xw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by SN7PR12MB8146.namprd12.prod.outlook.com (2603:10b6:806:323::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9564.10; Thu, 29 Jan 2026 13:32:32 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9542.010; Thu, 29 Jan 2026 13:32:31 +0000 From: Alexandre Courbot Date: Thu, 29 Jan 2026 22:32:05 +0900 Subject: [PATCH v5 5/7] rust: io: add `register!` macro Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260129-register-v5-5-c4587c902514@nvidia.com> References: <20260129-register-v5-0-c4587c902514@nvidia.com> In-Reply-To: <20260129-register-v5-0-c4587c902514@nvidia.com> To: Danilo Krummrich , Alice Ryhl , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Trevor Gross Cc: Yury Norov , John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TYCP286CA0361.JPNP286.PROD.OUTLOOK.COM (2603:1096:405:79::17) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH2PR12MB3990:EE_|SN7PR12MB8146:EE_ X-MS-Office365-Filtering-Correlation-Id: 76d7c65a-163b-4d0e-23db-08de5f3adabb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|10070799003|1800799024|7416014|376014|366016|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?YUtqOHEzR3ZTZnNrTXMzcFd5TUs1WFErNzcrTDRKcGxtVGZMUVdsME85UzRK?= =?utf-8?B?OFJwQy9VTEdwY3k0OUQ4dHA5ZzFKbk9vUU45bEc1dEFXTWgzYkpmOWxvYjVo?= =?utf-8?B?bXZkTVU5RXhPS0pvNndBc3R2eTFJN1pZY0NRWVNwVHowTS9hd29xMW9HOHNY?= =?utf-8?B?Z0dTWDZxcjRuYURDRjlsbVFLczlPbmh5RnhpM1VobzFvNTBENTNTZnlIZ0xY?= =?utf-8?B?WGlva3RkTDl2KytNMmpqMXBmbGxNeE1FdkRuRHpkd3J2ZHdOYzAyZCtXQkdE?= =?utf-8?B?SjM5dDAweUdMZzhMaC9udHN6RG5OYXJxdk9FdVFzVUpsRnZyd1ROOHJ2aHVX?= =?utf-8?B?WitRV2piaVVUTTA2MUlhOEw5ZUFDNWFCOU8zbElvMHBYUnBnVFREVXh0dWcw?= =?utf-8?B?ZUZGNHNvM0Ixbms5eGpqVjdWTzBiUjY1MUZYaytkK0xRRDE4WXRzTXVOY2hx?= =?utf-8?B?cW1vT0RrdTI3UDdrMlRXZk1XZFRDUnJxUGd6OElXdW5DdnlpcVVTa09sQnhQ?= =?utf-8?B?VndNNmJtUi84ZFZLekRoWmpZLzZ0eWtsd2tSWjFtMnpWZUprYm4rVytaTDVP?= =?utf-8?B?VUNBUUROQ0syendjOU1XalhxN3lEeDMrZmhkbUdCcWVxZlJkVEl3VU9lc0ZR?= =?utf-8?B?c2ZZTjByUFZwR2w5WVJWNlNJQ1V4emMwSVkyMlpFakZoV3ZKRGJkLzloZDA1?= =?utf-8?B?NDZCOExkMytkSGlUUkFxc3EvMXRNU0QvM3J1MDc2RU1RWUFJazF2NVk3Q08x?= =?utf-8?B?N1BIWUVIVDRVOUpSdkRtVTZIV1JpRFVCSWtjMUJrMStPaVYzMWJyckd0REFw?= =?utf-8?B?clNxOVY4a2QwZENFTTNDdXhLVkJjcFFlc01yVmEzeThzUFpIbWZDS2RFRm1B?= =?utf-8?B?cStuMGRobXJ3Rm1rL0RtbzdBSVhGMDg2NHhXZllBOUNWVVZyd3NWNkNqcWdQ?= =?utf-8?B?R1UwSWNrMC92bFBQVyt0cnlBUDVFUUN5cjNOb0F0ZjZhcHhJcENvSGFtZlRI?= =?utf-8?B?TUlTSzRJcGYrTkNjbG13UDF4YUpmNFZydExNQ2I5bEJRdU5hbXZ3L0hBNmxT?= =?utf-8?B?OEZ4SkVNWSs1RWRUU0Z3VGZ0QkYrSVJrQzJHeGZUK3VUOW1qQ2lZangyMGI4?= =?utf-8?B?cWs2NVZsUlFlS1NTRFFyQ01hQlByQSt5R0RSdVRKanlsSWJqYzBOQ1pScy92?= =?utf-8?B?akJiRzZmNVVvRjV4V0Fic2ZqOU8rei9JTjhQS2tQS1N3bmpJazBuWEFvVHJt?= =?utf-8?B?WmdDSEdqdnRLNWtRTVNnc2ZFUFhoc3dRL2ZmTVJkRExwWUNFaEVFSSt3Yjhs?= =?utf-8?B?R0Q4Q3VJRGhVTjR1dE84Vm93VVhINndRaStYUVU5Z28xNkVmTzYrcnpiU3Fi?= =?utf-8?B?YkF6TG9EczdVMS91QkROTEN4R2FTQnppaUd1UVMvMkhRVzFhYTE0Y2lkWjRN?= =?utf-8?B?OWVQTzhJMnJzWUxHTGx0enkvK3luMWlKZjllNFpjNVR2TlBNc1RFdjBMajNQ?= =?utf-8?B?S1ZVTmUzTy84YUZrZWp0eTlVT0E4OUtlYVMwSG4rdWQwN0pzaUQ5ZUpVUXZU?= =?utf-8?B?SFh0cDh1Slo4S0dFOWh5emRESFRsVGR6TE5BMkV2V3RKT1pSN3VkNkxNYWRr?= =?utf-8?B?SUNMOGxqSTByZ0JSU0VIM2FMVzlYTnpwbXYyTDBsMHQ2ZlJUbnUrMmh0ZnhS?= =?utf-8?B?RVhvYVRqUzNuU3RjbVdVanR4bllwbnhtdzlzb0YxQ1NYSXR1cXUxaWs4RVhU?= =?utf-8?B?akZUWWZzTWJFS2ZBcCtIUDVlenJhS2ZEY3kyWkJoNi9wMDVYbWM3WCs4cjVx?= =?utf-8?B?dlYxTzc2T1RHRDFlK3d2cEhLK3VTb2h4RmxjYUxoOXRaQ2V4NDVCSUtBcGNZ?= =?utf-8?B?NWN1OCt2L2FPNUxXVWtrbmYzVk13ZEpVeU5pT3BrT1hNeFd5YWo0N0k5akxL?= =?utf-8?B?czZWcEM3ZzRTQTNjd0pZVEtWSzlIWi95bFRXVnM5MUdGcDVvemx5b0Rod2ps?= =?utf-8?B?NE0rQ1gwSk1yTGNQVzI0L3lwUHNhUVpNT3FKRWd3QTNTRTdQaHJxZUpuYkZk?= =?utf-8?B?WFVkTGRZZVJnUFM3SlZXblBaS25EZU9Za0xqTEovNUJnVEp4RDlKcU96czNl?= =?utf-8?B?RTNJQldleDlaSlYxKy9HMWc3dTIvS3k4RVpFVlhtNDIwZW9ycGZuSUhoSkE2?= =?utf-8?B?U1E9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(10070799003)(1800799024)(7416014)(376014)(366016)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?M1RVRk1wNGZqYmw1TG8rZk1HMFFBdjdLcHNqdDI0S0pnK0s4akJxbE1MK1RC?= =?utf-8?B?bUw3ODB6WU92U2d3c2FLUEdQbVUxdE5lVXFzRkZOOUJBcU5vZDZpYUJIcXRT?= =?utf-8?B?MTJqbXZyVHpySnBSaHhZdjRwdVNkMjIzSHBwU25EbnR1bHFNTzBxdytNQWpn?= =?utf-8?B?UmJsKytUaEkrY1QyZzlUZ2RZeTc0NC9RY3dJa3dLVUZuOHRJcFFSQTNYNUwx?= =?utf-8?B?eHFTdElLQnJubmZwVm4xcmRnaTFvOTFuTlhQdGE4VlJ0ZFAxeUJSS21laU5m?= =?utf-8?B?ZEs5eHZGRFFueVVUV3hneHpkRDlwOXJGb1hnSE4vckxSMXJxNWI4VUZpRVJW?= =?utf-8?B?SGhRR1JITTh1cWQvK3RFblJNTEJNbEtsT3A0SkxTTmNmQ1BnQzFTY0VaeGFv?= =?utf-8?B?RjRta2t1VXdZY3I0MGlCWkpybEFBbmUxc0F6RHZnZU15T2pEYlhlbk51SThp?= =?utf-8?B?MGc0bVRYY3J3M0hwUi9KVHBiKzRha0R4YjZjbktNZkYwTnBSQ3NtVkdqNk4z?= =?utf-8?B?cnV4Y3pRbDZJL0tubGpNaVFlbFFWS0VOY0VBQTBoUlVuK2NlZW9qaktqMHR2?= =?utf-8?B?eW9DekwwZ3orYzZPanhOQ01tQ1JHMUFVVUx3bG01bTZwcWxzZUVHb1RRS29X?= =?utf-8?B?eUdsRTNza004UmppVHZuMjJPMkUxRlBFM1JYRUxVd3lUY1lNMlA1SFJtQ1RS?= =?utf-8?B?S1luRHJ3WitTNlJwd25Id05yWWYrME40dm1sMDl3Q09JYjh6c0JweVBuQnNH?= =?utf-8?B?MVprYnhybUtVbFNSUkFnYXVMOFV3R1BmNnBuVkRES3RDZWp1NnpCZTR4RWFj?= =?utf-8?B?S0g1aG5TWDMrZXAzL0pJTHFVdzlFRmpNbGJUeDhZSHJiUmxENXdqdVM2K1c1?= =?utf-8?B?T3dHV1dIakdZQ1lTVm55SXhKNldVdElJTm5sYmgzUkJEQnBYUDJuMU9CZElX?= =?utf-8?B?d3RqZlF3dEw3Tm14Ym5IR250amFEY1hOK3JibjdpOENMYmJLMTU1ZFhCOS9V?= =?utf-8?B?Zm5uVG5wbklIMDVFN2hyYlRpSCsrWmtTZm1kZWVadTB4dGJpREk3TUN5bVhO?= =?utf-8?B?TU54bkt5OVB5L0VNdTc3UVhqazJSeVBDZ0ZvcG5NREwzY3R1SHhZcVhIcmZn?= =?utf-8?B?QXo2Mk5SaHdvWW50N0FnQWtGT1JOMjZuSVQ2NVhYVkxLbWUvcDhKc1ArQmc5?= =?utf-8?B?TDFzN1dBUWdmOXVvenh5L1JQZzFsWXVkZ1RjVEUrREx4bUNLT3lPb3doeFFt?= =?utf-8?B?Tm5VLytrN1BXSTdEb0R6NWt4S0xpV1V4dUd1ZXlYMWIyOVU3bXZoR3Y1aTFt?= =?utf-8?B?UFVEbmdHL0g4ZjJybUtodkhTYW9CdVpXTk1hMHRnd3J0SUNGeVpYUENCb1lJ?= =?utf-8?B?Z3dnWDVuVVRsdUFHT3g3TWdJemhKT2VINVVWRWVOQkViek9DNmJtT3cwejFk?= =?utf-8?B?c2JabS9LNVdFbmJvZnBJRHNlRk1KQ0owZVY5U3pOQzVqd0hRT3VuSVZlWUNm?= =?utf-8?B?OW5OeHppWUtNekFUeUxRYUoxV3pBWVp2a2RidVJkUVNzdEdSeUFlanRTaEQ5?= =?utf-8?B?VHRhNGloRDVzRUlmNTZTTkc5YTdUTGtwbjNwRm4rYmpEVHoyKzlNdVNtampL?= =?utf-8?B?bXBZQ0VzeXh5SGI2Qjc2ZkFTQlZnelREQVUyQmV4Tlh2aXpKVVRnM0xsQlBD?= =?utf-8?B?WW5Ibk5CNVRqTUdYUjNoc2g0V2MrWnBtQXp1OUlhLzF0Ym5hRW5aeFF1ODRB?= =?utf-8?B?cG9RdEtYS1FKVzExM2I4Z2tWMTR6S2ZGT2w5VGVaek1LbnZ6SlE0TXlicVB2?= =?utf-8?B?WFJUN3E4RjJZZHh3OGQzeW5ZV1U5NmNHSVRsVjc5RGlnUURrMmYrOVhxb0hK?= =?utf-8?B?dnREVWUxK2hRb1VRbWtMQk94eXdwbE9Ka3N3clp5OXNKYnlQQU43WU5PWjRF?= =?utf-8?B?NnRpQnhsaVVmdkFETW1QSmIxNkhNMVBhRjR5cFVIenU0cGt1YlVoQnI4WjV6?= =?utf-8?B?dGE5UHh0S0o2YkZ3UVFlYXlzS0ova0QySDUxaUdXaksxV0pucjdQMDdVTUQ5?= =?utf-8?B?Y09kdGo5UHB2QkNMNmhDR2ptMXpkdnBLWXVPNEdFTG11cXNDRDRHaFo2VERi?= =?utf-8?B?OFd1bFJRZXJEYXQ0UUFNeDBjK3AxYW9NSlhkNnFvKzRZdUgrVzZ2WVF5dlI1?= =?utf-8?B?UEQxVFRNM3FSRG0vK3U3TjhlVTY0eVpGcHZ1MFdCN1pIeGJUZ1o0d05kdEV1?= =?utf-8?B?ZlB1dFY2NnM3RzFrc0xlYk1DOEM2TW8zTW9IYlV1OGt0dTBsWENRb2FGdVVo?= =?utf-8?B?Z2tMaXZZeWtGcU5ZRkg1K2NZU0ZndU1saDY0VGhUdzlPaGs5RGJMSlpjZTh1?= =?utf-8?Q?f37hKEn/nMFQco3MMlYDyUvH0/9QMf7hsvGzQWPZwneOW?= X-MS-Exchange-AntiSpam-MessageData-1: PzfJkdv1YN1nyg== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 76d7c65a-163b-4d0e-23db-08de5f3adabb X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Jan 2026 13:32:31.8687 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: OCYjLm6nxfwPOyu4iW67hgST7eaN+wE1zpT+LhG74eh57KPCsTiuh+0ObtnmAIUUTNH8TPJDyTzl6Qb1O/S5Mg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN7PR12MB8146 Add a macro for defining hardware register types with I/O accessors. Each register field is represented as a `Bounded` of the appropriate bit width, ensuring field values are never silently truncated. Fields can optionally be converted to/from custom types, either fallibly or infallibly. The address of registers can be direct, relative, or indexed, supporting most of the patterns in which registers are arranged. Tested-by: Dirk Behme Signed-off-by: Alexandre Courbot Suggested-by: Danilo Krummrich --- rust/kernel/io.rs | 1 + rust/kernel/io/register.rs | 1286 ++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 1287 insertions(+) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 056a3ec71647..112f43ecbf88 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -11,6 +11,7 @@ =20 pub mod mem; pub mod poll; +pub mod register; pub mod resource; =20 pub use resource::Resource; diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs new file mode 100644 index 000000000000..9d6c265e70c7 --- /dev/null +++ b/rust/kernel/io/register.rs @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! A macro to define register layout and accessors. +//! +//! A single register typically includes several fields, which are accesse= d through a combination +//! of bit-shift and mask operations that introduce a class of potential m= istakes, notably because +//! not all possible field values are necessarily valid. +//! +//! The [`register!`] macro in this module provides an intuitive and reada= ble syntax for defining a +//! dedicated type for each register. Each such type comes with its own fi= eld accessors that can +//! return an error if a field's value is invalid. +//! +//! [`register!`]: kernel::register! + +use crate::io::{ + IoCapable, + IoKnownSize, // +}; + +/// Trait providing a base address to be added to the offset of a relative= register to obtain +/// its actual offset. +/// +/// The `T` generic argument is used to distinguish which base to use, in = case a type provides +/// several bases. It is given to the `register!` macro to restrict the us= e of the register to +/// implementors of this particular variant. +pub trait RegisterBase { + /// Base address to which register offsets are added. + const BASE: usize; +} + +/// Trait providing I/O read/write operations for register storage types. +/// +/// This trait is implemented for all integer types on which I/O can be pe= rformed, allowing the +/// `register!` macro to generate appropriate I/O accessor methods based o= n the register's storage +/// type. +#[doc(hidden)] +pub trait RegisterIo: Sized { + /// Read a value from the given offset in the I/O region. + fn read(io: &I, offset: usize) -> Self + where + I: IoKnownSize + IoCapable; + + /// Write a value to the given offset in the I/O region. + fn write(self, io: &I, offset: usize) + where + I: IoKnownSize + IoCapable; +} + +impl RegisterIo for u8 { + #[inline(always)] + fn read(io: &I, offset: usize) -> Self + where + I: IoKnownSize + IoCapable, + { + io.read8(offset) + } + + #[inline(always)] + fn write(self, io: &I, offset: usize) + where + I: IoKnownSize + IoCapable, + { + io.write8(self, offset) + } +} + +impl RegisterIo for u16 { + #[inline(always)] + fn read(io: &I, offset: usize) -> Self + where + I: IoKnownSize + IoCapable, + { + io.read16(offset) + } + + #[inline(always)] + fn write(self, io: &I, offset: usize) + where + I: IoKnownSize + IoCapable, + { + io.write16(self, offset) + } +} + +impl RegisterIo for u32 { + #[inline(always)] + fn read(io: &I, offset: usize) -> Self + where + I: IoKnownSize + IoCapable, + { + io.read32(offset) + } + + #[inline(always)] + fn write(self, io: &I, offset: usize) + where + I: IoKnownSize + IoCapable, + { + io.write32(self, offset) + } +} + +impl RegisterIo for u64 { + #[inline(always)] + fn read(io: &I, offset: usize) -> Self + where + I: IoKnownSize + IoCapable, + { + io.read64(offset) + } + + #[inline(always)] + fn write(self, io: &I, offset: usize) + where + I: IoKnownSize + IoCapable, + { + io.write64(self, offset) + } +} + +/// Defines a dedicated type for a register, including getter and setter m= ethods for its fields and +/// methods to read and write it from an [`Io`] region. +/// +/// Example: +/// +/// ``` +/// use kernel::register; +/// +/// register! { +/// /// Basic information about the chip. +/// pub BOOT_0(u32) @ 0x00000100 { +/// /// Vendor ID. +/// 15:8 vendor_id; +/// /// Major revision of the chip. +/// 7:4 major_revision; +/// /// Minor revision of the chip. +/// 3:0 minor_revision; +/// } +/// } +/// ``` +/// +/// This defines a `BOOT_0` type which can be read from or written to offs= et `0x100` of an `Io` +/// region. For instance, `minor_revision` consists of the 4 least signifi= cant bits of the +/// register. +/// +/// Fields are instances of [`Bounded`](kernel::num::Bounded) and can be r= ead by calling their +/// getter method, which is named after them. They also have setter method= s prefixed with `with_` +/// for runtime values and `with_const_` for constant values. All setters = return the updated +/// register value. +/// +/// ```no_run +/// use kernel::register; +/// use kernel::num::Bounded; +/// +/// # register! { +/// # pub BOOT_0(u32) @ 0x00000100 { +/// # 15:8 vendor_id; +/// # 7:4 major_revision; +/// # 3:0 minor_revision; +/// # } +/// # } +/// # fn test>(bar= : &T) { +/// # fn obtain_vendor_id() -> u8 { 0xff } +/// // Read from the register's defined offset (0x100). +/// let boot0 =3D BOOT_0::read(&bar); +/// pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.m= inor_revision().get()); +/// +/// // Update some fields and write the new value back. +/// boot0 +/// // Constant values. +/// .with_const_major_revision::<3>() +/// .with_const_minor_revision::<10>() +/// // Run-time value. +/// .with_vendor_id(obtain_vendor_id()) +/// .write(&bar); +/// +/// // Or, just read and update the register in a single step. +/// BOOT_0::update(&bar, |r| r +/// .with_const_major_revision::<3>() +/// .with_const_minor_revision::<10>() +/// .with_vendor_id(obtain_vendor_id()) +/// ); +/// +/// // Constant values can also be built using the const setters. +/// const V: BOOT_0 =3D BOOT_0::zeroed() +/// .with_const_major_revision::<3>() +/// .with_const_minor_revision::<10>(); +/// # } +/// ``` +/// +/// Fields can also be transparently converted from/to an arbitrary type b= y using the bitfield `=3D>` +/// and `?=3D>` syntaxes. +/// +/// If present, doccomments above register or fields definitions are added= to the relevant item +/// they document (the register type itself, or the field's setter and get= ter methods). +/// +/// Note that multiple registers can be defined in a single `register!` in= vocation. This can be +/// useful to group related registers together. +/// +/// ``` +/// use kernel::register; +/// +/// register! { +/// pub BOOT_0(u8) @ 0x00000100 { +/// 7:4 major_revision; +/// 3:0 minor_revision; +/// } +/// +/// pub BOOT_1(u8) @ 0x00000101 { +/// 7:5 num_threads; +/// 4:0 num_cores; +/// } +/// }; +/// ``` +/// +/// It is possible to create an alias of an existing register with new fie= ld definitions by using +/// the `=3D> ALIAS` syntax. This is useful for cases where a register's i= nterpretation depends on +/// the context: +/// +/// ``` +/// use kernel::register; +/// +/// register! { +/// /// Scratch register. +/// pub SCRATCH(u32) @ 0x00000200 { +/// /// Raw value. +/// 31:0 value; +/// } +/// +/// /// Boot status of the firmware. +/// pub SCRATCH_BOOT_STATUS(u32) =3D> SCRATCH { +/// /// Whether the firmware has completed booting. +/// 0:0 completed; +/// } +/// } +/// ``` +/// +/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `S= CRATCH`, while also +/// providing its own `completed` field. +/// +/// ## Relative registers +/// +/// A register can be defined as being accessible from a fixed offset of a= provided base. For +/// instance, imagine the following I/O space: +/// +/// ```text +/// +-----------------------------+ +/// | ... | +/// | | +/// 0x100--->+------------CPU0-------------+ +/// | | +/// 0x110--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// | | +/// | | +/// 0x200--->+------------CPU1-------------+ +/// | | +/// 0x210--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// +-----------------------------+ +/// ``` +/// +/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset= `0x10` of their I/O +/// space segment. Since both instances of `CPU_CTL` share the same layout= , we don't want to define +/// them twice and would prefer a way to select which one to use from a si= ngle definition +/// +/// This can be done using the `Base + Offset` syntax when specifying the = register's address. +/// +/// `Base` is an arbitrary type (typically a ZST) to be used as a generic = parameter of the +/// [`RegisterBase`] trait to provide the base as a constant, i.e. each ty= pe providing a base for +/// this register needs to implement `RegisterBase`. Here is the abo= ve example translated +/// into code: +/// +/// ```no_run +/// use kernel::register; +/// use kernel::io::register::RegisterBase; +/// +/// // Type used to identify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize =3D 0x100; +/// } +/// // Singleton of `CPU0` used to identify it. +/// const CPU0: Cpu0 =3D Cpu0; +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize =3D 0x200; +/// } +/// // Singleton of `CPU1` used to identify it. +/// const CPU1: Cpu1 =3D Cpu1; +/// +/// # fn test>(bar= : &T) { +/// // This makes `CPU_CTL` accessible from all implementors of `RegisterB= ase`. +/// register! { +/// /// CPU core control. +/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 { +/// /// Start the CPU core. +/// 0:0 start; +/// } +/// } +/// +/// // The `read`, `write` and `update` methods of relative registers take= an extra `base` argument +/// // that is used to resolve its final address by adding its `BASE` to t= he offset of the +/// // register. +/// +/// // Start `CPU0`. +/// CPU_CTL::update(&bar, &CPU0, |r| r.with_start(true)); +/// +/// // Start `CPU1`. +/// CPU_CTL::update(&bar, &CPU1, |r| r.with_start(true)); +/// +/// // Aliases can also be defined for relative register. +/// register! { +/// /// Alias to CPU core control. +/// pub CPU_CTL_ALIAS(u32) =3D> CpuCtlBase + CPU_CTL { +/// /// Start the aliased CPU core. +/// 1:1 alias_start; +/// } +/// } +/// +/// // Start the aliased `CPU0`. +/// CPU_CTL_ALIAS::update(&bar, &CPU0, |r| r.with_alias_start(true)); +/// # } +/// ``` +/// +/// ## Arrays of registers +/// +/// Some I/O areas contain consecutive registers that can be interpreted i= n the same way. These +/// areas can be defined as an array of identical registers, allowing them= to be accessed by index +/// with compile-time or runtime bound checking. Simply specify their size= inside `[` and `]` +/// brackets, and add an `idx` parameter to their `read`, `write` and `upd= ate` methods: +/// +/// ```no_run +/// use kernel::register; +/// +/// # fn test>(bar= : &T) +/// # -> Result<(), Error>{ +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// // Array of 64 consecutive registers with the same layout starting at = offset `0x80`. +/// register! { +/// /// Scratch registers. +/// pub SCRATCH(u32)[64] @ 0x00000080 { +/// 31:0 value; +/// } +/// } +/// +/// // Read scratch register 0, i.e. I/O address `0x80`. +/// let scratch_0 =3D SCRATCH::read(&bar, 0).value(); +/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. +/// let scratch_15 =3D SCRATCH::read(&bar, 15).value(); +/// +/// // This is out of bounds and won't build. +/// // let scratch_128 =3D SCRATCH::read(&bar, 128).value(); +/// +/// // Runtime-obtained array index. +/// let scratch_idx =3D get_scratch_idx(); +/// // Access on a runtime index returns an error if it is out-of-bounds. +/// let some_scratch =3D SCRATCH::try_read(&bar, scratch_idx)?.value(); +/// +/// // Alias to a particular register in an array. +/// // Here `SCRATCH[8]` is used to convey the firmware exit code. +/// register! { +/// /// Firmware exit status code. +/// pub FIRMWARE_STATUS(u32) =3D> SCRATCH[8] { +/// 7:0 status; +/// } +/// } +/// let status =3D FIRMWARE_STATUS::read(&bar).status(); +/// +/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. +/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the +/// // registers of the two declarations below are interleaved. +/// register! { +/// /// Scratch registers bank 0. +/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride =3D 8] @ 0x000000c0 { +/// 31:0 value; +/// } +/// +/// /// Scratch registers bank 1. +/// pub SCRATCH_INTERLEAVED_1(u32)[16, stride =3D 8] @ 0x000000c4 { +/// 31:0 value; +/// } +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Relative arrays of registers +/// +/// Combining the two features described in the sections above, arrays of = registers accessible from +/// a base can also be defined: +/// +/// ```no_run +/// use kernel::register; +/// use kernel::io::register::RegisterBase; +/// +/// # fn test>(bar= : &T) +/// # -> Result<(), Error>{ +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// // Type used as parameter of `RegisterBase` to specify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize =3D 0x100; +/// } +/// // Singleton of `CPU0` used to identify it. +/// const CPU0: Cpu0 =3D Cpu0; +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize =3D 0x200; +/// } +/// // Singleton of `CPU1` used to identify it. +/// const CPU1: Cpu1 =3D Cpu1; +/// +/// // 64 per-cpu scratch registers, arranged as a contiguous array. +/// register! { +/// /// Per-CPU scratch registers. +/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 { +/// 31:0 value; +/// } +/// } +/// +/// let cpu0_scratch_0 =3D CPU_SCRATCH::read(&bar, &Cpu0, 0).value(); +/// let cpu1_scratch_15 =3D CPU_SCRATCH::read(&bar, &Cpu1, 15).value(); +/// +/// // This won't build. +/// // let cpu0_scratch_128 =3D CPU_SCRATCH::read(&bar, &Cpu0, 128).value(= ); +/// +/// // Runtime-obtained array index. +/// let scratch_idx =3D get_scratch_idx(); +/// // Access on a runtime value returns an error if it is out-of-bounds. +/// let cpu0_some_scratch =3D CPU_SCRATCH::try_read(&bar, &Cpu0, scratch_i= dx)?.value(); +/// +/// // `SCRATCH[8]` is used to convey the firmware exit code. +/// register! { +/// /// Per-CPU firmware exit status code. +/// pub CPU_FIRMWARE_STATUS(u32) =3D> CpuCtlBase + CPU_SCRATCH[8] { +/// 7:0 status; +/// } +/// } +/// let cpu0_status =3D CPU_FIRMWARE_STATUS::read(&bar, &Cpu0).status(); +/// +/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. +/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the +/// // registers of the two declarations below are interleaved. +/// register! { +/// /// Scratch registers bank 0. +/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride =3D 8] @ CpuCtlBase = + 0x00000d00 { +/// 31:0 value; +/// } +/// +/// /// Scratch registers bank 1. +/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride =3D 8] @ CpuCtlBase = + 0x00000d04 { +/// 31:0 value; +/// } +/// } +/// # Ok(()) +/// # } +/// ``` +/// [`Io`]: kernel::io::Io +#[macro_export] +macro_rules! register { + // Entry point for the macro, allowing multiple registers to be define= d in one call. + // It matches all possible register declaration patterns to dispatch t= hem to corresponding + // `@reg` rule that defines a single register. + ( + $( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + $([ $size:expr $(, stride =3D $stride:expr)? ])? + $(@ $($base:ident +)? $offset:literal)? + $(=3D> $alias:ident $(+ $alias_offset:ident)? $([$alias_id= x:expr])? )? + { $($fields:tt)* } + )* + ) =3D> { + $( + $crate::register!( + @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = =3D $stride)?])? + $(@ $($base +)? $offset)? + $(=3D> $alias $(+ $alias_offset)? $([$alias_idx])? )? + { $($fields)* } + ); + )* + }; + + // All the rules below are private helpers. + + // Creates a register at a fixed offset of the MMIO space. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offse= t:literal + { $($fields:tt)* } + ) =3D> { + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_fixed $name($storage) @ $offset); + }; + + // Creates an alias register of fixed offset register `alias` with its= own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $al= ias:ident + { $($fields:tt)* } + ) =3D> { + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_fixed $name($storage) @ $alias::OFFSET); + }; + + // Creates a register at a relative offset from a base address provide= r. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:= ident + $offset:literal + { $($fields:tt)* } + ) =3D> { + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_relative $name($storage) @ $base + $offset ); + }; + + // Creates an alias register of relative offset register `alias` with = its own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $ba= se:ident + $alias:ident + { $($fields:tt)* } + ) =3D> { + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_relative $name($storage) @ $base + $alias::O= FFSET ); + }; + + // Creates an array of registers at a fixed offset of the MMIO space. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + [ $size:expr, stride =3D $stride:expr ] @ $offset:literal { $(= $fields:tt)* } + ) =3D> { + static_assert!(::core::mem::size_of::<$storage>() <=3D $stride); + + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_array $name($storage) [ $size, stride =3D $s= tride ] @ $offset); + }; + + // Shortcut for contiguous array of registers (stride =3D=3D size of e= lement). + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:= expr ] @ $offset:literal + { $($fields:tt)* } + ) =3D> { + $crate::register!( + $(#[$attr])* $vis $name($storage) [ $size, stride =3D ::core::= mem::size_of::<$storage>() ] + @ $offset { $($fields)* } + ); + }; + + // Creates an alias of register `idx` of array of registers `alias` wi= th its own fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $al= ias:ident [ $idx:expr ] + { $($fields:tt)* } + ) =3D> { + static_assert!($idx < $alias::SIZE); + + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!(@io_fixed $name($storage) @ $alias::OFFSET + $id= x * $alias::STRIDE); + }; + + // Creates an array of registers at a relative offset from a base addr= ess provider. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + [ $size:expr, stride =3D $stride:expr ] + @ $base:ident + $offset:literal { $($fields:tt)* } + ) =3D> { + static_assert!(::core::mem::size_of::<$storage>() <=3D $stride); + + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!( + @io_relative_array $name($storage) [ $size, stride =3D $stride= ] @ $base + $offset + ); + }; + + // Shortcut for contiguous array of relative registers (stride =3D=3D = size of element). + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:= expr ] + @ $base:ident + $offset:literal { $($fields:tt)* } + ) =3D> { + $crate::register!( + $(#[$attr])* $vis $name($storage) [ $size, stride =3D ::core::= mem::size_of::<$storage>() ] + @ $base + $offset { $($fields)* } + ); + }; + + // Creates an alias of register `idx` of relative array of registers `= alias` with its own + // fields. + ( + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + =3D> $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)*= } + ) =3D> { + static_assert!($idx < $alias::SIZE); + + $crate::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields= )* } + ); + $crate::register!( + @io_relative $name($storage) @ $base + $alias::OFFSET + $idx *= $alias::STRIDE + ); + }; + + // Generates the bitfield for the register. + // + // `#[allow(non_camel_case_types)]` is added since register names typi= cally use SCREAMING_CASE. + ( + @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:t= y) { $($fields:tt)* } + ) =3D> { + $crate::register!(@bitfield_core + #[allow(non_camel_case_types)] + $(#[$attr])* $vis $name $storage + ); + $crate::register!(@bitfield_fields $vis $name $storage { $($fields= )* }); + }; + + // Generates the IO accessors for a fixed offset register. + (@io_fixed $name:ident ($storage:ty) @ $offset:expr) =3D> { + #[allow(dead_code)] + impl $name { + /// Absolute offset of the register. + pub const OFFSET: usize =3D $offset; + + /// Read the register from its address in `io`. + #[inline(always)] + pub fn read(io: &T) -> Self where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + let io =3D io.deref(); + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, Self::OFFSET)) + } + + /// Write the value contained in `self` to the register addres= s in `io`. + #[inline(always)] + pub fn write(self, io: &T) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + let io =3D io.deref(); + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, Self::OFFSET) + } + + /// Read the register from its address in `io` and run `f` on = its value to obtain a new + /// value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + f: F, + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io)); + reg.write(io); + } + } + }; + + // Generates the IO accessors for a relative offset register. + (@io_relative $name:ident ($storage:ty) @ $base:ident + $offset:expr )= =3D> { + #[allow(dead_code)] + impl $name { + /// Relative offset of the register. + pub const OFFSET: usize =3D $offset; + + /// Read the register from `io`, using the base address provid= ed by `base` and adding + /// the register's offset to it. + #[inline(always)] + pub fn read( + io: &T, + #[allow(unused_variables)] + base: &B, + ) -> Self where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + let io =3D io.deref(); + let offset =3D >::BASE + Self::OFFSET; + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the register's offset to it. + #[inline(always)] + pub fn write( + self, + io: &T, + #[allow(unused_variables)] + base: &B, + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + let io =3D io.deref(); + let offset =3D >::BASE + Self::OFFSET; + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the register from `io`, using the base address provid= ed by `base` and adding + /// the register's offset to it, then run `f` on its value to = obtain a new value to + /// write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + base: &B, + f: F, + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, base)); + reg.write(io, base); + } + } + }; + + // Generates the IO accessors for an array of registers. + (@io_array $name:ident ($storage:ty) [ $size:expr, stride =3D $stride:= expr ] + @ $offset:literal) =3D> { + #[allow(dead_code)] + impl $name { + /// Absolute offset of the register array. + pub const OFFSET: usize =3D $offset; + /// Number of elements in the array of registers. + pub const SIZE: usize =3D $size; + /// Number of bytes separating each element of the array of re= gisters. + pub const STRIDE: usize =3D $stride; + + /// Read the array register at index `idx` from its address in= `io`. + #[inline(always)] + pub fn read( + io: &T, + idx: usize, + ) -> Self where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + build_assert!(idx < Self::SIZE); + + let io =3D io.deref(); + let offset =3D Self::OFFSET + (idx * Self::STRIDE); + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to the array register = with index `idx` in `io`. + #[inline(always)] + pub fn write( + self, + io: &T, + idx: usize + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + build_assert!(idx < Self::SIZE); + + let io =3D io.deref(); + let offset =3D Self::OFFSET + (idx * Self::STRIDE); + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a + /// new value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + idx: usize, + f: F, + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, idx)); + reg.write(io, idx); + } + + /// Read the array register at index `idx` from its address in= `io`. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_read( + io: &T, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + if idx < Self::SIZE { + Ok(Self::read(io, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Write the value contained in `self` to the array register = with index `idx` in `io`. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_write( + self, + io: &T, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + { + if idx < Self::SIZE { + Ok(self.write(io, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a + /// new value to write back. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn try_update( + io: &T, + idx: usize, + f: F, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + F: ::core::ops::FnOnce(Self) -> Self, + { + if idx < Self::SIZE { + Ok(Self::update(io, idx, f)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + } + }; + + // Generates the IO accessors for an array of relative registers. + ( + @io_relative_array $name:ident ($storage:ty) [ $size:expr, stride = =3D $stride:expr ] + @ $base:ident + $offset:literal + ) =3D> { + #[allow(dead_code)] + impl $name { + /// Relative offset of the register array. + pub const OFFSET: usize =3D $offset; + /// Number of elements in the array of registers. + pub const SIZE: usize =3D $size; + /// Number of bytes separating each element of the array of re= gisters. + pub const STRIDE: usize =3D $stride; + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it. + #[inline(always)] + pub fn read( + io: &T, + #[allow(unused_variables)] + base: &B, + idx: usize, + ) -> Self where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + build_assert!(idx < Self::SIZE); + + let io =3D io.deref(); + let offset =3D >::BASE + + Self::OFFSET + (idx * Self::STRIDE); + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the offset of array register `idx` to it. + #[inline(always)] + pub fn write( + self, + io: &T, + #[allow(unused_variables)] + base: &B, + idx: usize + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + build_assert!(idx < Self::SIZE); + + let io =3D io.deref(); + let offset =3D >::BASE + + Self::OFFSET + (idx * Self::STRIDE); + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it, then run= `f` on its value to + /// obtain a new value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + base: &B, + idx: usize, + f: F, + ) where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, base, idx)); + reg.write(io, base, idx); + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_read( + io: &T, + base: &B, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + if idx < Self::SIZE { + Ok(Self::read(io, base, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the offset of array register `idx` to it. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_write( + self, + io: &T, + base: &B, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + { + if idx < Self::SIZE { + Ok(self.write(io, base, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it, then run= `f` on its value to + /// obtain a new value to write back. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn try_update( + io: &T, + base: &B, + idx: usize, + f: F, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref, + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$st= orage>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + if idx < Self::SIZE { + Ok(Self::update(io, base, idx, f)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + } + }; + + // Defines the wrapper `$name` type and its conversions from/to the st= orage type. + (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) = =3D> { + $(#[$attr])* + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + $vis struct $name($storage); + + #[allow(dead_code)] + impl $name { + /// Creates a bitfield from a raw value. + $vis const fn from_raw(value: $storage) -> Self { + Self(value) + } + + /// Creates a zeroed bitfield value. + /// + /// This is a const alternative to the `Zeroable::zeroed()` tr= ait method. + $vis const fn zeroed() -> Self { + Self(0) + } + + /// Returns the raw value of this bitfield. + /// + /// This is similar to the [`From`] implementation, but is sho= rter to invoke in + /// most cases. + $vis const fn as_raw(self) -> $storage { + self.0 + } + } + + // SAFETY: `$storage` is `Zeroable` and `$name` is transparent. + unsafe impl ::pin_init::Zeroable for $name {} + + impl ::core::convert::From<$name> for $storage { + fn from(val: $name) -> $storage { + val.as_raw() + } + } + + impl ::core::convert::From<$storage> for $name { + fn from(val: $storage) -> $name { + Self::from_raw(val) + } + } + }; + + // Definitions requiring knowledge of individual fields: private and p= ublic field accessors, + // and `Debug` implementation. + (@bitfield_fields $vis:vis $name:ident $storage:ty { + $($(#[doc =3D $doc:expr])* $hi:literal:$lo:literal $field:ident + $(?=3D> $try_into_type:ty)? + $(=3D> $into_type:ty)? + ; + )* + } + ) =3D> { + #[allow(dead_code)] + impl $name { + $( + $crate::register!(@private_field_accessors $vis $name $storage : $= hi:$lo $field); + $crate::register!( + @public_field_accessors $(#[doc =3D $doc])* $vis $name $storag= e : $hi:$lo $field + $(?=3D> $try_into_type)? + $(=3D> $into_type)? + ); + )* + } + + $crate::register!(@debug $name { $($field;)* }); + }; + + // Private field accessors working with the correct `Bounded` type for= the field. + ( + @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt= :$lo:tt $field:ident + ) =3D> { + ::kernel::macros::paste!( + $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive =3D $lo..=3D$hi; + $vis const [<$field:upper _MASK>]: $storage =3D + ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1); + $vis const [<$field:upper _SHIFT>]: u32 =3D $lo; + ); + + ::kernel::macros::paste!( + fn [<__ $field>](self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> { + // Left shift to align the field's MSB with the storage MSB. + const ALIGN_TOP: u32 =3D $storage::BITS - ($hi + 1); + // Right shift to move the top-aligned field to bit 0 of the s= torage. + const ALIGN_BOTTOM: u32 =3D ALIGN_TOP + $lo; + + // Extract the field using two shifts. `Bounded::shr` produces= the correctly-sized + // output type. + let val =3D ::kernel::num::Bounded::<$storage, { $storage::BIT= S }>::from( + self.0 << ALIGN_TOP + ); + val.shr::() + } + + const fn [<__with_ $field>]( + mut self, + value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>, + ) -> Self + { + const MASK: $storage =3D $name::[<$field:upper _MASK>]; + const SHIFT: u32 =3D $name::[<$field:upper _SHIFT>]; + + let value =3D value.into_inner() << SHIFT; + self.0 =3D (self.0 & !MASK) | value; + + self + } + ); + }; + + // Public accessors for fields infallibly (`=3D>`) converted to a type. + ( + @public_field_accessors $(#[doc =3D $doc:expr])* $vis:vis $name:id= ent $storage:ty : + $hi:literal:$lo:literal $field:ident =3D> $into_type:ty + ) =3D> { + ::kernel::macros::paste!( + + $(#[doc =3D $doc])* + #[doc =3D "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> $into_type + { + self.[<__ $field>]().into() + } + + $(#[doc =3D $doc])* + #[doc =3D "Sets this field to the given `value`."] + #[inline(always)] + $vis fn [](self, value: $into_type) -> Self + { + self.[<__with_ $field>](value.into()) + } + + ); + }; + + // Public accessors for fields fallibly (`?=3D>`) converted to a type. + ( + @public_field_accessors $(#[doc =3D $doc:expr])* $vis:vis $name:id= ent $storage:ty : + $hi:tt:$lo:tt $field:ident ?=3D> $try_into_type:ty + ) =3D> { + ::kernel::macros::paste!( + + $(#[doc =3D $doc])* + #[doc =3D "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> + Result< + $try_into_type, + <$try_into_type as ::core::convert::TryFrom< + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + >>::Error + > + { + self.[<__ $field>]().try_into() + } + + $(#[doc =3D $doc])* + #[doc =3D "Sets this field to the given `value`."] + #[inline(always)] + $vis fn [](self, value: $try_into_type) -> Self + { + self.[<__with_ $field>](value.into()) + } + + ); + }; + + // Public accessors for fields not converted to a type. + ( + @public_field_accessors $(#[doc =3D $doc:expr])* $vis:vis $name:id= ent $storage:ty : + $hi:tt:$lo:tt $field:ident + ) =3D> { + ::kernel::macros::paste!( + + $(#[doc =3D $doc])* + #[doc =3D "Returns the value of this field."] + #[inline(always)] + $vis fn $field(self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + { + self.[<__ $field>]() + } + + $(#[doc =3D $doc])* + #[doc =3D "Sets this field to the compile-time constant `VALUE`."] + #[inline(always)] + $vis const fn [](self) = -> Self { + self.[<__with_ $field>]( + ::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new= ::() + ) + } + + $(#[doc =3D $doc])* + #[doc =3D "Sets this field to the given `value`."] + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> Self + where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo= }>>, + { + self.[<__with_ $field>](value.into()) + } + + $(#[doc =3D $doc])* + #[doc =3D "Tries to set this field to `value`, returning an error = if it is out of range."] + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> ::kernel::error::Result + where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $= lo }>, + { + Ok( + self.[<__with_ $field>]( + value.try_into_bounded().ok_or(::kernel::error::code::= EOVERFLOW)? + ) + ) + } + + ); + }; + + // `Debug` implementation. + (@debug $name:ident { $($field:ident;)* }) =3D> { + impl ::kernel::fmt::Debug for $name { + fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kerne= l::fmt::Result { + f.debug_struct(stringify!($name)) + .field("", &::kernel::prelude::fmt!("{:#x}", self= .0)) + $( + .field(stringify!($field), &self.$field()) + )* + .finish() + } + } + }; +} --=20 2.52.0