From patchwork Thu Mar 20 13:39:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Courbot X-Patchwork-Id: 14024002 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 5DD30C28B30 for ; Thu, 20 Mar 2025 13:40:05 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 98FD810E627; Thu, 20 Mar 2025 13:40:04 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.b="a/dlY2qp"; dkim-atps=neutral Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12on2048.outbound.protection.outlook.com [40.107.244.48]) by gabe.freedesktop.org (Postfix) with ESMTPS id C2D4C10E61B; Thu, 20 Mar 2025 13:39:56 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=CYQZ+KHxGR1ehdNDwLUvkhH1rIQuueHL3ZXmAUBkTF1qAMSWFlgI0n3+XwYDvhx0pS6+LgVZ/zBymbLyYUYlSP6uLlc2/5kAWdvpdAW5mOrU5zozs8L2Al4GoEiZ5dF7EqQQ0GCDYC7NsuDe918AkL7il0aAytgViLRJZ9zyQsPtOwgPcDFN8V6SxVui+SFFUkTmcyBfKwDco6Cm6ZWXiXQ9sClr+rZPDFwztp4pHj8pNMrQvFeJxGOIMswU2yFX/MsoJ7REQakx99t/qWUW+G07S1IQj0DNBxEAIyNWWxaNnAY94KXyjA6dw1rUykRwsrRtolJdgKLT8LuJiZXNXw== 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=UGVZG1xRBatSmCFEwlJEeNReMyfGhf7QLcPFnC3KbB0=; b=gy+DsZAFgoIecz4bolxxN8dSJhpO4f6d3gNaZt7P5bOkTOFvErqRnYzTvuamNxc/b/egCHQEL0BpdV1gd7RtF+p1Vs26PKcg/t74lz+KfhKg7GO/k+suPcgepDEvRU/1ojIUgkBo1M8X+UJVQBBM+Tsr7hPRZhKwknzD4T5LAtdsTMgz1tQvSrhy0zJfKjpP7MIkjyLiqRo18uVTE3+guMnruPcMAiDpemJw73k3RwIYtWWxtWFWmHpdBwibOV3uVz+9NMxfx2lLfnCOkvB5+E1PHOlQk4MqT0uCpyHYN9pfzPymIDo/OtQhmA3xuEkCoUZyyUQ8z9eulc0WPJkCVw== 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=UGVZG1xRBatSmCFEwlJEeNReMyfGhf7QLcPFnC3KbB0=; b=a/dlY2qp4jYS4i9L4IyCQZtdhZOVkhM3hpFDFjXKAD08bJzvQ4lZ6kOgU2QQ8ScuiJB3iFkdPScVU+TfiA5qzW9aX8PQgy6v7Gz8E0quxIniNbOAW1puq4yHvu+CFGEC4xANZfqjShZOpDE1Eqk8gmA5tN0dOJNctvUGJFZ6hDem6gPo8+uv7/Mk77jZiz/xCGy6IOoh5efpRRMt07KZnMXFgJpfKZs/gL6m8UJxT15wo/E4GqinYmeqUQA0sNsNHO5LVjVvLjZgfyvGj2RILW9e7iZUoF7JGvodH2mz8gEcSpp0LIk7Lzp58A1xUUfALCuZ+cqalM+iqbdUzDQrew== 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 IA1PR12MB6409.namprd12.prod.outlook.com (2603:10b6:208:38b::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8534.35; Thu, 20 Mar 2025 13:39:52 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::6e37:569f:82ee:3f99]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::6e37:569f:82ee:3f99%6]) with mapi id 15.20.8534.034; Thu, 20 Mar 2025 13:39:52 +0000 From: Alexandre Courbot Date: Thu, 20 Mar 2025 22:39:14 +0900 Subject: [PATCH RFC v3 6/7] gpu: nova-core: add basic timer device Message-Id: <20250320-nova_timer-v3-6-79aa2ad25a79@nvidia.com> References: <20250320-nova_timer-v3-0-79aa2ad25a79@nvidia.com> In-Reply-To: <20250320-nova_timer-v3-0-79aa2ad25a79@nvidia.com> To: Danilo Krummrich , David Airlie , John Hubbard , Ben Skeggs , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Simona Vetter Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, Alexandre Courbot X-Mailer: b4 0.14.2 X-ClientProxiedBy: TYCP286CA0271.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:3c9::16) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH2PR12MB3990:EE_|IA1PR12MB6409:EE_ X-MS-Office365-Filtering-Correlation-Id: 8d33a1b5-adbc-4f94-153a-08dd67b4b0ef X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|366016|10070799003|7416014|376014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?q?VI1eEsiSl9GvkHnGgl0qqzEahg6I2ul?= =?utf-8?q?n+Ze/yfvED4+m/NbJGz4ych+ybz6gfU2Con7jdOefq2FuVtVeJpxA8YOOspPqfZ0D?= =?utf-8?q?0xLz4Pypja4/x6Kf/WhTdrHTVv5xRzitSiqsEhWhiYpEQCstkYBinFQd4NAM5S1LA?= =?utf-8?q?HJQCTOacHGx/KA1xBwf2lAMn1nUVKERZf4Fv6u31Qv4dL7gkyxqK33UYt5gE1x0DV?= =?utf-8?q?b10WPFV9n0sN9MV9ftjLZbbeb4XgGbM8PGAcIA/MC/+9P1GxVt0ZnZgK4/k3pZVsj?= =?utf-8?q?d+0OwWxxWQzUTXJNFQKj1O7d1x4s9DfFGPMWQu5zgCiPPmrISyhpt5xpkSjEKFfTp?= =?utf-8?q?Jb27SgDDuOdG53mpTUa/a13DhIoMPpv5ViQSaIaxj7QHq9r3OezR4ANxvxE/tBJsq?= =?utf-8?q?BzCDjv13Pd9z7Y6pwJ29eaJIIbub7w2dvBd9tH7IEq5nAN9CauLAquBDAlLVGY90W?= =?utf-8?q?rQ5y+JCrDAtbghiv3CxmweD51wwx3ZWnmSFPIlIkWk5plpHMF8zqWpZwcu0r4rqnt?= =?utf-8?q?zaQ1XJ3GlCCr/cV9FglxFKxbM3yvT6Em7/nIggi/qVwhQb3mHIs3MQV7I8JZ26k5l?= =?utf-8?q?UQtx/6GndjMNnhEXUtVUYy7C3cabINRXgi/YvqIrY9z6DJYPW7ce/QhWEQnzpljgM?= =?utf-8?q?VQvdkLZ//QlIhJ8up5N27kQntZJbVIdKEWuCIKZJ2+57Pf/gTQXTc/zNxciKtkgwl?= =?utf-8?q?3TcCAzj460ky48ZXaQm1Q6hIx1ZYha7NlUIE9fUpj+x/HHDijxBGsjg6cxQfokoC9?= =?utf-8?q?s34vtPllbQttbOGkttfPTFJD8cJJIcvE/5iDGF0RLL+UBLL6hPvwTWs0cwud/q/1g?= =?utf-8?q?N9nSWTV15q0mk04HR9Wg/EE7dL1YFEIToYwSKc7d+azz6Zhh526H9E7809E7CiYAi?= =?utf-8?q?cQ4YJ/4TGhTQwszZ4NLPr2M6NwYsopZFpfdFJARKBbHq3uBAoRY2AirrBc7s1iwJk?= =?utf-8?q?i7tDX7Hsatp5sP/A+gCRNUr0lBiN36BBVRHtsHOWU1jkbcIhqUGZStDeqXJsWP/2Y?= =?utf-8?q?0hfmbcAMHLMF/9KwU2DNt4MjSPxyaEK547jyDwkwKmPaSqDUlqVjT24SHO7eA9o+N?= =?utf-8?q?9XgkmAbmaBE9Kpejp/YHmv10ed8f8zZoihOc4dddoedl9GEvjXO/erN5m3JFrGwr4?= =?utf-8?q?+hm2gDPUZhvZoNSCvlbPblMWC294bgha2vcBZgn4qHDowmOu7Dg/AzXx25I18Jtk6?= =?utf-8?q?1tUeMuvR6BOkU+xWJrnuPI03XvGVgC3iMr1qRURzOd7ePKw9leM8oZvzY7ilSIQj6?= =?utf-8?q?nWYB+YizMcIDWFFQchyats8KioYlCU39f/5gb9cEA3ROtfDE1oPYNxWHo1qqgCBQ8?= =?utf-8?q?hB0P0yNhwUqjN7qLUmUQ6kLL+z/821cu1n91VPVWRUqUzYjTD2wq4+M=3D?= 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)(1800799024)(366016)(10070799003)(7416014)(376014)(921020); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?OWx5LBThEnzYJ6ovxrR2wDNK3Xuw?= =?utf-8?q?Vr6Ifej+NYIY98mn4Ee54W20V5vCf7/FvN1N/Xsk+LPy5wgpEwjyG4stIfl5TI+Ie?= =?utf-8?q?Vp1j7z+QLq4pAucKfqbSjMuTJX2edqKII9/75AGN98i9PbHdZz/Xtt84sNR4RkyeW?= =?utf-8?q?9ZciyISIF1wa9DgMi1MZMd88W1coR7oRKWK6H48OSAWoxR/DzVXCzkTjIDu78iK+q?= =?utf-8?q?/CF0/8LQZ2WjHS7y11/6IakAQ2PMgdKTb2/EjZmbpWTkKHiArwttvBTIpl6A8ycEn?= =?utf-8?q?SvdpkHzzwmz4CjIaTAZc/6rCbF0ZNLgPUVFLmytTYHD8Mer4rzmh9jaMrVOlJfYZU?= =?utf-8?q?U9S38iX0ehakuPodTcIa0m90N/ATTtyHUNAkM0faOjPvfXVPxMv29JxhGbN6iV/OB?= =?utf-8?q?EjUlkpMI+yk+PcVuC93RiKRlPvPi6m/RXZDUkd7ilPneI2+CfgpUQ40G/x6c1+6qX?= =?utf-8?q?BRjq/ArRt1lm987h8QS2UbkouZ4x9o5l0tn8DRNy4F8zK9Dc+k8HJDcUoUug2Lq1n?= =?utf-8?q?FIQMQ6agiLz+8KeEhRBhIo/Mpq3loVI5vzrDcQOKAm20JhxzoN/8hQXEcmQF6k1Zp?= =?utf-8?q?LHxHGAArfetVIlbG0pjNwYwwopvbu4Kk1D631XTjNaOWgBV0N5qAr2RXnICcHGZwI?= =?utf-8?q?cawkK/D6Wk7/63JXqTR5xZcAXBQCBp/WXJf1Od05mGJQsaDykUyQei6pVDuSKDdMi?= =?utf-8?q?bF2EBZg4J1R4RqvLJSxr6gzVWuTQGnS6SL0oNK8eWSeEUzASs22LBtEo9LHV8arNG?= =?utf-8?q?DNsdOHpI8tXCA/Qskl9j64kQPph2/FD8BJ6a0XioWQrkf48zU81bKu+gyRkRoZr3w?= =?utf-8?q?gtFeqJq0sxTYA1CQq1yzex0cSiqovxWly6JHwb9El+cleNOLrVdPwZiriIcqAc/jG?= =?utf-8?q?PFV9MSZhNT+ASTj63WaSckdlWt76OYZFIEvFjgOYh02eRMPgKH/AaPxfsYfh98oDS?= =?utf-8?q?7wydRSUqS/c0/fRMAvf2rIip/TeujZ/igJ/PFfgNWGUyvL/dggbM+yeaqul+P0qfG?= =?utf-8?q?uDZMSwQlGcjatv6xJop5aCyqZWqhdjnnNdkrjajnnjwoVWiabGsNJi7v+FfG0Etb8?= =?utf-8?q?ys4QnXc+8DlqJ0DcfnthzfQKxHoRcRn1TYHPeSwT8swPpCSlQZkiknNsXRFLmOoLd?= =?utf-8?q?UHQzltOqehJfsuBCqas8AaDV+Z80lhw279Yp2RpeYaJRFKVOS+sOP+3wSdXUFyDfI?= =?utf-8?q?zcuQuQAuy+TiO265KXI/G8CRKCu8bs14jZDVYF09Mq/44ebx2v10Xd90Fm+XOw9S+?= =?utf-8?q?TzQ4cfAeCeQP2JygJMKFxNodzL2S/1x97GhR+oOnRpelTxUD4LLX+RsUkTrofUv44?= =?utf-8?q?36pkVv+8pQkADLBtQopchf7IabD7UCs0V200pB/UQro9ljND0hS38towooDHMxzDw?= =?utf-8?q?l1RXHcCxkVJueYkjF684mELWOEnGYLaxt5TzHPfxteJ+gB1HWGOkfkTwfqOwGtA/3?= =?utf-8?q?6eynAmUZwJaPVfKVrTSZe3kAywZ4ufBYQRX/Ye4tzlB+yEhgtYFajhdQJmwNYhw0C?= =?utf-8?q?2xfzjum+J2GIQc7Gyykk1tgjwnKue+IMbl5LrnrSBbF/WspasAWcpmqVJIfVpRz2O?= =?utf-8?q?xDUaSsFOpdi?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8d33a1b5-adbc-4f94-153a-08dd67b4b0ef X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Mar 2025 13:39:51.9529 (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: EyuGXeowYvf5rRJF0zetCvd/lFmSV9jpqFS2v6dcc8LZg26/Qx91SaPUhci1DkMlkhpvJwHY8OUWcsplLvC8Mg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA1PR12MB6409 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Add a basic timer device and exercise it during device probing. This first draft is probably very questionable. One point in particular which should IMHO receive attention: the generic wait_on() method aims at providing similar functionality to Nouveau's nvkm_[num]sec() macros. Since this method will be heavily used with different conditions to test, I'd like to avoid monomorphizing it entirely with each instance ; that's something that is achieved in nvkm_xsec() using functions that the macros invoke. I have tried achieving the same result in Rust using closures (kept as-is in the current code), but they seem to be monomorphized as well. Calling extra functions could work better, but looks also less elegant to me, so I am really open to suggestions here. Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/driver.rs | 4 +- drivers/gpu/nova-core/gpu.rs | 55 +++++++++++++++- drivers/gpu/nova-core/nova_core.rs | 1 + drivers/gpu/nova-core/regs.rs | 11 ++++ drivers/gpu/nova-core/timer.rs | 132 +++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 63c19f140fbdd65d8fccf81669ac590807cc120f..0cd23aa306e4082405f480afc0530a41131485e7 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -10,7 +10,7 @@ pub(crate) struct NovaCore { pub(crate) gpu: Gpu, } -const BAR0_SIZE: usize = 8; +const BAR0_SIZE: usize = 0x9500; pub(crate) type Bar0 = pci::Bar; kernel::pci_device_table!( @@ -42,6 +42,8 @@ fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result> GFP_KERNEL, )?; + let _ = this.gpu.test_timer(); + Ok(this) } } diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index d96901e5c8eace1e7c57c77da7def209e8149cd3..f010d3152530b1cec032ca620e59de51a2fc1a13 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -6,8 +6,10 @@ use crate::driver::Bar0; use crate::regs; +use crate::timer::Timer; use crate::util; use core::fmt; +use core::time::Duration; macro_rules! define_chipset { ({ $($variant:ident = $value:expr),* $(,)* }) => @@ -179,6 +181,7 @@ pub(crate) struct Gpu { /// MMIO mapping of PCI BAR 0 bar: Devres, fw: Firmware, + timer: Timer, } impl Gpu { @@ -194,6 +197,56 @@ pub(crate) fn new(pdev: &pci::Device, bar: Devres) -> Result Result<()> { + pr_info!("testing timer subdev\n"); + with_bar!(self.bar, |b| { + pr_info!("current timestamp: {}\n", self.timer.read(b)) + })?; + + if !matches!( + self.timer + .wait_on(&self.bar, Duration::from_millis(10), || Some(())), + Ok(()) + ) { + pr_crit!("timer test failure\n"); + } + + let t1 = with_bar!(self.bar, |b| { + pr_info!("timestamp after immediate exit: {}\n", self.timer.read(b)); + self.timer.read(b) + })?; + + if self + .timer + .wait_on(&self.bar, Duration::from_millis(10), || Option::<()>::None) + != Err(ETIMEDOUT) + { + pr_crit!("timer test 2 failure\n"); + } + + let t2 = with_bar!(self.bar, |b| self.timer.read(b))?; + if t2 - t1 < Duration::from_millis(10) { + pr_crit!("timer test 3 failure\n"); + } + + with_bar!(self.bar, |b| { + pr_info!( + "timestamp after timeout: {} ({:?})\n", + self.timer.read(b), + t2 - t1 + ); + })?; + + Ok(()) } } diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 94f4778c16f6a4d046c2f799129ed0cc68df6fd4..f54dcfc66490cb6b10090ef944ac14feca9f6972 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -18,6 +18,7 @@ macro_rules! with_bar { mod firmware; mod gpu; mod regs; +mod timer; mod util; kernel::module_pci_driver! { diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 7bfd2b575fe2184565d495012e55cd0829b0b1ad..0d06e09b1ba62d55688c633500f37d3fe1aeb30e 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -11,3 +11,14 @@ 7:4 major_rev => as u8, "major revision of the chip"; 28:20 chipset => try_into Chipset, "chipset model" ); + +/* PTIMER */ + +register!(PtimerTime0@0x00009400; + 31:0 lo => as u32, "low 32-bits of the timer" +); + +register!(PtimerTime1@0x00009410; + 31:0 hi => as u32, "high 32 bits of the timer" +); + diff --git a/drivers/gpu/nova-core/timer.rs b/drivers/gpu/nova-core/timer.rs new file mode 100644 index 0000000000000000000000000000000000000000..1361e4ad10d923ce114972889cdfcfa6e58a691b --- /dev/null +++ b/drivers/gpu/nova-core/timer.rs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Nova Core Timer subdevice + +use core::fmt::Display; +use core::ops::{Add, Sub}; +use core::time::Duration; + +use kernel::devres::Devres; +use kernel::num::U64Ext; +use kernel::prelude::*; + +use crate::driver::Bar0; +use crate::regs; + +/// A timestamp with nanosecond granularity obtained from the GPU timer. +/// +/// A timestamp can also be substracted to another in order to obtain a [`Duration`]. +/// +/// TODO: add Kunit tests! +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct Timestamp(u64); + +impl Display for Timestamp { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Add for Timestamp { + type Output = Self; + + fn add(mut self, rhs: Duration) -> Self::Output { + let mut nanos = rhs.as_nanos(); + while nanos > u64::MAX as u128 { + self.0 = self.0.wrapping_add(nanos as u64); + nanos -= u64::MAX as u128; + } + + Timestamp(self.0.wrapping_add(nanos as u64)) + } +} + +impl Sub for Timestamp { + type Output = Duration; + + fn sub(self, rhs: Self) -> Self::Output { + Duration::from_nanos(self.0.wrapping_sub(rhs.0)) + } +} + +pub(crate) struct Timer {} + +impl Timer { + pub(crate) fn new() -> Self { + Self {} + } + + /// Read the current timer timestamp. + pub(crate) fn read(&self, bar: &Bar0) -> Timestamp { + loop { + let hi = regs::PtimerTime1::read(bar); + let lo = regs::PtimerTime0::read(bar); + + if hi.hi() == regs::PtimerTime1::read(bar).hi() { + return Timestamp(u64::from_u32s(hi.hi(), lo.lo())); + } + } + } + + #[allow(dead_code)] + pub(crate) fn time(bar: &Bar0, time: u64) { + regs::PtimerTime1::default() + .set_hi(time.upper_32_bits()) + .write(bar); + regs::PtimerTime0::default() + .set_lo(time.lower_32_bits()) + .write(bar); + } + + /// Wait until `cond` is true or `timeout` elapsed, based on GPU time. + /// + /// When `cond` evaluates to `Some`, its return value is returned. + /// + /// `Err(ETIMEDOUT)` is returned if `timeout` has been reached without `cond` evaluating to + /// `Some`, or if the timer device is stuck for some reason. + pub(crate) fn wait_on Option>( + &self, + bar: &Devres, + timeout: Duration, + cond: F, + ) -> Result { + // Number of consecutive time reads after which we consider the timer frozen if it hasn't + // moved forward. + const MAX_STALLED_READS: usize = 16; + + let (mut cur_time, mut prev_time, deadline) = { + let cur_time = with_bar!(bar, |b| self.read(b))?; + let deadline = cur_time + timeout; + + (cur_time, cur_time, deadline) + }; + let mut num_reads = 0; + + loop { + if let Some(ret) = cond() { + return Ok(ret); + } + + (|| { + cur_time = with_bar!(bar, |b| self.read(b))?; + + /* Check if the timer is frozen for some reason. */ + if cur_time == prev_time { + if num_reads >= MAX_STALLED_READS { + return Err(ETIMEDOUT); + } + num_reads += 1; + } else { + if cur_time >= deadline { + return Err(ETIMEDOUT); + } + + num_reads = 0; + prev_time = cur_time; + } + + Ok(()) + })()?; + } + } +}