From patchwork Tue Mar 4 13:54:00 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Courbot X-Patchwork-Id: 14000772 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 078ECC282C6 for ; Tue, 4 Mar 2025 13:55:16 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 24FD010E5D0; Tue, 4 Mar 2025 13:55:10 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.b="d4o0IT06"; dkim-atps=neutral Received: from NAM12-DM6-obe.outbound.protection.outlook.com (mail-dm6nam12on2082.outbound.protection.outlook.com [40.107.243.82]) by gabe.freedesktop.org (Postfix) with ESMTPS id B399E10E5D7; Tue, 4 Mar 2025 13:55:08 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=DISAcKVtGr/m6HMx00YtVSLik9XLXf61ui/mbrvEG+KfdxxNU/z1XX0eK0ACjkj27rwFfoVywQOmuJZYtKxm298i8d92BjIWzguK/9F/PhYBdQDKjtsJakZc6oH/Bb51+qj/8I26mU+6n1LnDY21SLewlrH+BE724CIekVUivd0c5cT6SmOiBjBeFDd/TalF2QT68urU/cFiBRgg8igsXt4SBGU1q1hz8q5XB0jGcsposkxiage81FW0vI4khOeqnk1N33zbvBOSnqDdgmarpXyu3StQEswIuOodm+H7WchhhvhAQxrIVlCASBtx611Hk47arKysEpY4fVJaesaTbw== 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=wrk9cVRDcIB3ketMyLZPufB/GYlGfdXAsIYEsarYDHc=; b=JekWVmWIk5cDfhKO/w3hTyEJFkRAlAw0w2wRQejMooS6nMeWB92zS42jk1EbW0HmGoOvDaQljfAhSW/Ck+GUXq57RcG5MF/LVwsO5vvKMlsA6r3aOFNPIlzIQ7ua7gjeyLw/3dvChbwMTN2Ib7JlmtRTgiFuGY3W5Ul1a77ULP5j1APHFU7CwD5a3gEKyUft0xwRHvM3oWGrcUSqfjUpxJzboXfVHhYzPQEQxM7v+Nx4PK+bw6wYJQ2FpjTiqE7Pb3Vo2WrtTHHcrbEo2wnH4UZIgPhjc2hXZsd+d3QdqLk+NccQ3idDrzcAImtqbIVlqZdyOT6EjoPQ0p4+ctRrkw== 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=wrk9cVRDcIB3ketMyLZPufB/GYlGfdXAsIYEsarYDHc=; b=d4o0IT06ySKWfSWlz0qNkapHIwo21xqNBQ4gBA8zCnzxdcct6vJ9Tq3U2EcLZqIT4WVOyt76qO2g9eqsoInAA566WT02R3cSXkGnbkl8sIDALgth2311Z8PZKRSRoxGshUuVgDJ2IcMaigUB/3hnsEPLtZp8Z99kyvbDP1lSFTDu8NE4/oysjkwez49selSz3mBpHkgRcDzQYfiLDwDSaT7KW5PRu4CH65m+N3azb9TlYnR10MrF2nme3jlEYDBCgUFFn+7yBP3fUdkeltH/JyIA5kR/YX6OfdMfMOVrl8Z2JKGSVZ65V7NyCUaj+SjGisBuOFvPi1NDcYKykNGUTQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from MN2PR12MB3997.namprd12.prod.outlook.com (2603:10b6:208:161::11) by DS0PR12MB7874.namprd12.prod.outlook.com (2603:10b6:8:141::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8489.25; Tue, 4 Mar 2025 13:55:05 +0000 Received: from MN2PR12MB3997.namprd12.prod.outlook.com ([fe80::d161:329:fdd3:e316]) by MN2PR12MB3997.namprd12.prod.outlook.com ([fe80::d161:329:fdd3:e316%6]) with mapi id 15.20.8489.025; Tue, 4 Mar 2025 13:55:05 +0000 From: Alexandre Courbot Date: Tue, 04 Mar 2025 22:54:00 +0900 Subject: [PATCH RFC v2 4/5] gpu: nova-core: add basic timer device Message-Id: <20250304-nova_timer-v2-4-8fb13f3f8cff@nvidia.com> References: <20250304-nova_timer-v2-0-8fb13f3f8cff@nvidia.com> In-Reply-To: <20250304-nova_timer-v2-0-8fb13f3f8cff@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: TYXPR01CA0050.jpnprd01.prod.outlook.com (2603:1096:403:a::20) To MN2PR12MB3997.namprd12.prod.outlook.com (2603:10b6:208:161::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: MN2PR12MB3997:EE_|DS0PR12MB7874:EE_ X-MS-Office365-Filtering-Correlation-Id: 1a2917b3-ae80-43ca-540e-08dd5b242afe X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|10070799003|366016|7416014|376014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?q?o4xCvZbYF3oLU819LoncwLNPyyt3/yB?= =?utf-8?q?U+W2bnwuMm54hyg75M23naY2RBTArdnnEdb+wlkBU680/NXwYzbOBcU0sgYcyR9wt?= =?utf-8?q?GNRk3gyhXjzh8SyL0MxN8FvadM+IFs/j0esxHx/AbzrLT8tLhwgLrKi7iuMegKkfV?= =?utf-8?q?5D7yVBgZh8rwHLod36mn5WwjiJCyLEMsd4Xi/PfEbNrIMJy0xtmXeooVFKkmuJam2?= =?utf-8?q?2EEnmZFfD19V3vEo2ARPl9Kfo7xNpShXotd1mvff+asfE9+9nzZGKuWPOqSYJoY4T?= =?utf-8?q?Dyhq/PRvt5WKI7t94U3fgnxqvqUThl9vZmdHFG3KfVwo0EE+9aSD0aJFlGDMBDuij?= =?utf-8?q?U0QQkvyRQJkRsW2350pXCcbarYzZ7cxRHw2MNfXg17bQgAoWeeX9Wy0N007Jsh4h2?= =?utf-8?q?iRRr4DHld7qzGeyrxpN+OGW76m8OWz7ysK19Of8nz/fEQUzuRGkWeHyJSEq5eDf54?= =?utf-8?q?RjvQLuXu8hgxC5qnUGCyv1MBpYoFakMaBBuAJBu1264QXXG1+YIoBCjeX3r2izybz?= =?utf-8?q?z+SCQsCgLcXF8TaFbrJ9+yHaI5bpOpoTNk+kPdlhhTgIxu3F5mgIigXAF1/qeGa3e?= =?utf-8?q?EJIDGmUQAG9P6gqGicdsrotD/ogJIfUBt4WOa2N+kp+ugR6NCiyRovoE73ckiCI/L?= =?utf-8?q?Q3cDe2IaGtdHYaPk+kGr0PinHJEcNymO2UL8P/3wWTnkJkr2CosVRZAYdSwjVfwC5?= =?utf-8?q?16wTK4VNT7FPhtzUWD2pMtIApGeR0WaPnETnBu71/fuQ07sr9GvxgyxKBj0KjlIj2?= =?utf-8?q?F+5ehMtcDV4nlVb7zfso61/oUZuSTCEtbw/Dl/LY3GvCjwHYSg5//uORbY1eA4qzl?= =?utf-8?q?zsZVMH8jca8rDFl6LEzS2SJncNuP2jnHr2BRDupadlBiW5LsvFtYofWTrxC+FEC4u?= =?utf-8?q?DhRlG6HPHRzMh6AU0dhGUmpP0m2X1R8bP3UEptznnu4OVJ7ImtMxV4dr+XK73tAuk?= =?utf-8?q?jByokPEO3Od1g3yiKwLVsJM9Swau3IpuXZaZrI37ZIQZsSNywXFey7Z3uCv3baQY5?= =?utf-8?q?Cd6w+z+zdkMbfygtae0gT2IOcRV5/38pOh8uODlw3eHFOI+S+P+nxZLRYwoZDXdoJ?= =?utf-8?q?9vIfZr7nuqxqj31fy92sxFccLIiyLaJ6paqOFI01lqcZaAZxAbulx1PvnLDj6SXhU?= =?utf-8?q?wpHz/UzE9w/KAp1L/ZclFmeRPVqm16RW5CoCev1elQ1EHQ5aYTZjAxlNZ/4lujX+K?= =?utf-8?q?tpiDwJNzRCC4B/Dh+0zouev6WZUWJbV4GkIQlYl/JSfAk6E7CrIVDnyx3HtzXdeT/?= =?utf-8?q?cKgjQ6Ac1lPf+jLNTM3bRTN0R+M+4CcHuapbRAuOMmiYh0W53wNrqyCa23Q2OAOvg?= =?utf-8?q?MzkHoGPrt0+yscU4f/vPDERjsNtqsLE9kxTx1CjWeIincwl3nx+1xVw=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:MN2PR12MB3997.namprd12.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(1800799024)(10070799003)(366016)(7416014)(376014)(921020); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?U8DHHlXclhrFtUf3M1J66mW9tuAr?= =?utf-8?q?N9hIcBtTkvdDIx4SsXRnteoVamgFhmWwtdrOulcH2Wb5lfhU3ll7sHFBFft/TAy4T?= =?utf-8?q?STqShxuTpT/NOuH+Y3LuKkcnPuga23WylwRr34vVPiTWt41TeXRXPpJSyNXmZyjiY?= =?utf-8?q?nD/9QcUShqJDZSzORfYZqryQ0a79Ir+EqawXJ0WXECGhfxyfI4f58yGYRte29ouNQ?= =?utf-8?q?B8cw5JxECJlbFC9fcQ8BziBKE/9lTTV6BquZczj2tnRHOlTxxov4pztoSVpf8ZK0B?= =?utf-8?q?xb4wiJQ+pMco8otkUjYVa9AzeeokQ9ZXZYoha7i73J630aGs+ylFjzxN1JBfyHLlV?= =?utf-8?q?f71kK2lsK0cCe9aaX1LljcZtZO8/WqHsiBnBphjLqZKKxaHVLh73zGrmKcgOWyhjv?= =?utf-8?q?ms80VorZvis3PRJzTyj9IZqNFym1xNS1428Y70xhJtj3r/gEC5GYNVmJl8zJ80pRY?= =?utf-8?q?FvylCIaLtKVonbmreh6wm5ra1mZI4rVH3ryLCiurmYdIxF9U2GkoLdZZ7w0NCiTFn?= =?utf-8?q?pOJ7v27Vojr2KZqeog8eeOUwnOu86QoeU71NT8VLWR+55og+hrZxmZOxIpKpJXSFM?= =?utf-8?q?QbIAyF0yC1+/7/Rk6RPl+285irKjb3zPQNTDyFQHjv/NICD9u6hL/LpHuy19xln9U?= =?utf-8?q?NQCpz+FPuhz13HeNqkpZLP7nNNo8axRqoD8sIsJCtRBMTlfnORCJon4mfLNLOUQKA?= =?utf-8?q?a+YpEdWq6VFhnJ2Ilt6TA9QSpR/Xhv5FAXSsOF0elltbwL8PLyizC2TiMrz9DEScx?= =?utf-8?q?96rEUyMrukmKxsuMpru2cPDtB7ppYb99b+7m11bejl09k/1WE/JgljK3fRnxT9894?= =?utf-8?q?GpVfC99NNePSNkvX5hG1SfmeXdySzWwJi+EfCRXcFydnMR11zF3ecOkvdsr2ttomw?= =?utf-8?q?/W4J+QhegLHt+yHIAMSH9fNWCK7PDkY6J7ltGdKoWIzGrndNzosNzO1QnG7nw/0kG?= =?utf-8?q?wlEm39HxJUJafisHUe9DPYUhXKGgpYqYsw9f0MpNOj2j+6faAjhEcIm06AOWVjKEp?= =?utf-8?q?VV8ylsmpPcfmZclLpX1nkjnPLgrfFwH3ka3/KUP/GgYcRnggu5okFRz1Gp/d0/KWE?= =?utf-8?q?Nf4N0KxJnjfCtEkoUzynHxr0G5P5UiTq+Mzsmc42hDEW6nqqLB1w5rY6jEaaaoVIM?= =?utf-8?q?AtyhQt0LkRuJEDDIvYMoWLDR7upjcwg8XMa2oJgJkzh7JUpdkqSDXD2Wk7tiULcvi?= =?utf-8?q?TC5bLrmzBMpXvq2CeORq+wzipNwl8ZihVo+Z7ujAEsdNApPSHI5qwEu/ZCe6oHjFN?= =?utf-8?q?hx+ysie+qILrHdi+HcJxtcvUfD8cPViuGzaBMDGvzaKmaDvj9cwGTkcKKKRNZbNMT?= =?utf-8?q?+0TBsGa5awydXVEsxhJb6qZCOevsm04ZyAWK8OsbuBHq234k6YEAqbjtgzr6fjtuX?= =?utf-8?q?L+g6OFwb22glomb5XmxVJgLqxAQMmQHFWJ7TiMQLONHWIQcBXt393q/Q6+dM43LVC?= =?utf-8?q?YdaAq8u3YoFti4nE3W4k/Pb+p1YmWpvkGZ6grSa+l0/NKdimq39QzopIsBYMpCacI?= =?utf-8?q?uV4x7hOc5zVjOVvkFW+wcc5mmsstMb8PjY11FE/wVU/xPY0Qa5OIgFL3m1soCxEN5?= =?utf-8?q?xqSYux7XjSc?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1a2917b3-ae80-43ca-540e-08dd5b242afe X-MS-Exchange-CrossTenant-AuthSource: MN2PR12MB3997.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Mar 2025 13:55:05.7285 (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: 6lLeteIk9bhzelM/Q5G+5UrAs4BvLUC7IaNn8SPzOAZtGDhXYSFJhFps9/pCpUr2xhJqZkmgVFOJxSFAb/3wKw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DS0PR12MB7874 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 | 58 ++++++++++++++++- drivers/gpu/nova-core/nova_core.rs | 1 + drivers/gpu/nova-core/regs.rs | 8 +++ drivers/gpu/nova-core/timer.rs | 124 +++++++++++++++++++++++++++++++++++++ 5 files changed, 193 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 58b97c7f0b2ab1edacada8346b139f6336b68272..8fa8616c0deccc7297b090fcbe74f3cda5cc9741 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -1,12 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 +use kernel::device::Device; +use kernel::types::ARef; use kernel::{ device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::BStr, str::CString, }; use crate::driver::Bar0; use crate::regs; +use crate::timer::Timer; use core::fmt; +use core::time::Duration; const fn to_lowercase_bytes(s: &str) -> [u8; N] { let src = s.as_bytes(); @@ -201,10 +205,12 @@ fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result { /// Structure holding the resources required to operate the GPU. #[pin_data] pub(crate) struct Gpu { + dev: ARef, spec: Spec, /// MMIO mapping of PCI BAR 0 bar: Devres, fw: Firmware, + timer: Timer, } impl Gpu { @@ -220,6 +226,56 @@ pub(crate) fn new(pdev: &pci::Device, bar: Devres) -> Result Result<()> { + let bar = self.bar.try_access().ok_or(ENXIO)?; + dev_info!(&self.dev, "testing timer subdev\n"); + dev_info!(&self.dev, "current timestamp: {}\n", self.timer.read(&bar)); + drop(bar); + + assert!(matches!( + self.timer + .wait_on(&self.bar, Duration::from_millis(10), || Some(())), + Ok(()) + )); + + let bar = self.bar.try_access().ok_or(ENXIO)?; + dev_info!( + &self.dev, + "timestamp after immediate exit: {}\n", + self.timer.read(&bar) + ); + let t1 = self.timer.read(&bar); + drop(bar); + + assert_eq!( + self.timer + .wait_on(&self.bar, Duration::from_millis(10), || Option::<()>::None), + Err(ETIMEDOUT) + ); + + let bar = self.bar.try_access().ok_or(ENXIO)?; + let t2 = self.timer.read(&bar); + assert!(t2 - t1 >= Duration::from_millis(10)); + dev_info!( + &self.dev, + "timestamp after timeout: {} ({:?})\n", + self.timer.read(&bar), + t2 - t1 + ); + drop(bar); + + Ok(()) } } diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 8479be2a3f31798e887228863f223d42a63bd8ca..891a93ba7656d2aa5e1fa4357d1d84ee3a054942 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -6,6 +6,7 @@ mod firmware; mod gpu; mod regs; +mod timer; kernel::module_pci_driver! { type: driver::NovaCore, diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index a874cb2fa5bedee258a60e5c3b471f52e5f82469..35bbd3c0b58972de3a2478ef20f93f31c69940e7 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -172,3 +172,11 @@ impl Builder<$name> { 7:4 major_rev as (u8), "major revision of the chip"; 25:20 chipset try_into (Chipset), "chipset model" ); + +nv_reg!(PtimerTime0@0x00009400; + 31:0 lo as (u32), "low 32-bits of the timer" +); + +nv_reg!(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..919995bf32141c568206fda165dcac6f4d4ce8b8 --- /dev/null +++ b/drivers/gpu/nova-core/timer.rs @@ -0,0 +1,124 @@ +// 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(self, rhs: u64) -> Self::Output { + Timestamp(self.0.wrapping_add(rhs)) + } +} + +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::new().hi(time.upper_32_bits()).write(bar); + regs::PtimerTime0::new().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, + dev_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 bar = dev_bar.try_access().ok_or(ENXIO)?; + let cur_time = self.read(&bar); + let deadline = cur_time + u64::try_from(timeout.as_nanos()).unwrap_or(u64::MAX); + + (cur_time, cur_time, deadline) + }; + let mut num_reads = 0; + + loop { + if let Some(ret) = cond() { + return Ok(ret); + } + + (|| { + let bar = dev_bar.try_access().ok_or(ENXIO)?; + cur_time = self.read(&bar); + + /* 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(()) + })()?; + } + } +}