From nobody Mon Sep 28 23:12:41 2026 Received: from mail-43172.protonmail.ch (mail-43172.protonmail.ch [185.70.43.172]) (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 7FD7F3DDDDA for ; Sat, 15 Aug 2026 10:25:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789536; cv=none; b=r4TD7mrYYuS/oyDHAigBY50kTJzXjLeZzIHXkVRqkJt3cY6bNA0f2kN8konh5PHwmBw1haEsc72D9Myh4chlCCDgXCVo5nLGDSrAW9yNdJgumLA91WiMzjGWNfTI9fg/U6VubPi0cvgQG/tukvLVmPiVf/moD4Wf/OZ4ieSbVqQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789536; c=relaxed/simple; bh=f620hvyXPCGL/uxNApOHGdvi/wX5f7xc6L68aofgPLc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OGJ94P+J+OW4k1bnpMqiWaza0ApcCK2X9JJcRJIOpVkIwocFMq0rk6ro4GKZmCUKbtWgokpj9DftnWaUamt0T8LQ6pJO7P4gYCwN/BcKzfXH27CCojDNQNl0j2ElmJfcWkFt1AueEl5yp3UmgdMPxlP4H2Fdg5EzE8dXYgFq4+w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=YoVENFNM; arc=none smtp.client-ip=185.70.43.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="YoVENFNM" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=protonmail; t=1786789528; x=1787048728; bh=+v9o+NRXK5CCGWPth43KlQ7r3Y+hk4xRaz/0tYZOqNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=YoVENFNM/6GiXMCKoKVf/e8x5o4OFa0+8WpY0bLj3FsNsFUNuD+0yNZEj+ikg//Ep d7h8CycnaDLbGDi3JkH+NAw97lLbkfliYBkk2xmlZOQwMMJg5/b7kADUOY5jh/K4ua kXI9fFsztOAEAQAzEOtDwa8IMkxXIRk/D+WXTWHRmNM0zVEkVLRR9+I7MRlmJkdY/V 6yuHLyV+oBbQZBkcs7NYpzDXMqAg7W7N6XJlv0fyERJ+zND1GvOFI3yuVfLBs6vKac GLUhuPYXoDnGIfuU699Ad0NIXoTAOK6+hlMr+cpp7PXZy/PHvLDHBhUcFP+T0ZOSoQ gDiW2L+/yNcBA== X-Pm-Submission-Id: 4hMZwx5vMnz2SchQ From: =?UTF-8?q?Onur=20=C3=96zkan?= To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: dakr@kernel.org, aliceryhl@google.com, daniel.almeida@collabora.com, airlied@gmail.com, simona@ffwll.ch, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v4 1/4] rust: workqueue: impl Send and Sync for OwnedQueue Date: Sat, 15 Aug 2026 13:23:57 +0300 Message-ID: <20260815-tyr-reset-impl-v4-1-578df9a5e576@onurozkan.dev> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> References: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Allows OwnedQueue to be used across thread boundaries which is needed from the tyr reset implementation. Reviewed-by: Daniel Almeida Signed-off-by: Onur =C3=96zkan --- rust/kernel/workqueue/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/workqueue/mod.rs b/rust/kernel/workqueue/mod.rs index 933032fc4a85..d766cd9b9175 100644 --- a/rust/kernel/workqueue/mod.rs +++ b/rust/kernel/workqueue/mod.rs @@ -383,6 +383,14 @@ pub struct OwnedQueue { queue: NonNull, } =20 +// SAFETY: `OwnedQueue` has exclusive ownership of the workqueue and acces= ses to workqueues +// are thread safe as documented by the `Send` implementation for `Queue`. +unsafe impl Send for OwnedQueue {} + +// SAFETY: Shared access to an `OwnedQueue` only provides shared access to= its `Queue` which +// is thread safe as documented by the `Sync` implementation for `Queue`. +unsafe impl Sync for OwnedQueue {} + impl Deref for OwnedQueue { type Target =3D Queue; fn deref(&self) -> &Queue { --=20 2.51.2 From nobody Mon Sep 28 23:12:41 2026 Received: from mail-106112.protonmail.ch (mail-106112.protonmail.ch [79.135.106.112]) (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 F15763DDDC0; Sat, 15 Aug 2026 10:25:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.135.106.112 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789540; cv=none; b=q1peLCYmcQQctv59tz3EXtpzGrxWt4fcEqBdIww7j0nao0/NyUvh/82o4pY77kW6Ohl2bHEdtbBgbJdnFYbqHjEDPEA8FQa80ZdaMP6HuvjupSzMQODLg8KNaoYiu8G6ap2pbfTlYUILq3mu0JfoAhZo8Xr1XpW5Jgj7baprYLs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789540; c=relaxed/simple; bh=KLMHjdeOZqPVCAsOuVuCXyRkdDrSv8YL7ZS+nZzLkOQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AyFtcgs9BdABOfATJMqOgepDROJf3dJEdBf4eat6Dhw/LxMR4uXesypsCyxvkRYBqoKr/O6nDKMbQZ+QnFeQ/XMc9EE5/ugZIyxqMvCXJUHc9Fq3LyR2ark20j5q80Vu8SOCuvSt5zvs+PTeq8CjVOqcGfCd0Xkc1nTmegtzR2Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=vPhRndMy; arc=none smtp.client-ip=79.135.106.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="vPhRndMy" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=protonmail; t=1786789528; x=1787048728; bh=hMJu3QgsZs8TCdcBeB9Zz/yumkYg03H4j+Dga41n/50=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=vPhRndMy9KD2r5MHARQytGEK15dboU97MzGNv8jlwXblN+D8bloHu9zGj1s2r2b2x Y89J259p5Nlo2qybKhGlCeOBez6XMZlI8rchECoUkQXQ6Y6/hbn+K0fq3nnwj00Baz LOgvOV0WbYIISXoJBsGRJAiiQTLvAIkotKgxooRC47SFWlmcoibLTVP1lyNodOCREE Qw4nr2BW6pBj0/rFtxXrXbcmCIN/BEzXCRDKDE2dQX/gkhq92x8VgM/bsaTNRGD8jM ZFJxgJAj2tm/o28Ip61j0/wwTaC6pRKFIDDcg3+vxh/DtIme9ZBN6/3lN0k1DIFth6 R/l7PYaVuqJfQ== X-Pm-Submission-Id: 4hMZx044trz2SchM From: =?UTF-8?q?Onur=20=C3=96zkan?= To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: dakr@kernel.org, aliceryhl@google.com, daniel.almeida@collabora.com, airlied@gmail.com, simona@ffwll.ch, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, =?UTF-8?q?Onur=20=C3=96zkan?= , Boris Brezillon Subject: [PATCH v4 2/4] drm/tyr: clear stale IRQ state before soft reset Date: Sat, 15 Aug 2026 13:23:58 +0300 Message-ID: <20260815-tyr-reset-impl-v4-2-578df9a5e576@onurozkan.dev> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> References: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Previous reset may leave the reset completed IRQ set which can make the poll return too early. Clear the IRQ first so the driver waits for the current reset to complete. Reviewed-by: Daniel Almeida Reviewed-by: Boris Brezillon Signed-off-by: Onur =C3=96zkan --- drivers/gpu/drm/tyr/driver.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index c5063b2be94d..90d6cd988cd2 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -86,6 +86,9 @@ pub(crate) struct TyrDrmRegistrationData<'bound> { } =20 fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result { + // Clear any stale reset IRQ state before issuing a new soft reset. + iomem.write_reg(GPU_IRQ_CLEAR::zeroed().with_reset_completed(true)); + iomem.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset)); =20 poll::read_poll_timeout( --=20 2.51.2 From nobody Mon Sep 28 23:12:41 2026 Received: from mail-106111.protonmail.ch (mail-106111.protonmail.ch [79.135.106.111]) (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 530023DF008 for ; Sat, 15 Aug 2026 10:25:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.135.106.111 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789545; cv=none; b=P7IAfIEvTQ3sQcox08CXFUX316nS05ny056LEbCUiuIh/gbmyGoXiu4JkYW6keWTEZQyJGyOeGScmy+hOPFYkGiG2bgZX/paQJ++ZKMmgJJExXF7DzOP+qK3Efe74ydBZ1UCqbHx0fR9Pf/O0hZmUpB86T13BzLUBjgLb85WGMs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789545; c=relaxed/simple; bh=3VvhECSURBGiMgAw61zfU8fHF8Hwc6eppBF9K0/8pKs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ufj16cxoDqMgCkNovXGDoJxa1vy28S9WqcvyC7mnp3VlcfUWf0/kpEirGqd76+rsfSFj6xbWS7EAj/jcZOognGoj4X2o0fUuvDmcp/iwYrt9Nv6Bn+sq+kaSjL+9ucyr9zaKwDbOkw/DAR3QtPcXlG990hHzYMDRJ+kWQrSasVM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=iCNYgAbl; arc=none smtp.client-ip=79.135.106.111 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="iCNYgAbl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=protonmail; t=1786789530; x=1787048730; bh=yPUaHWI6nviLvPmjRl0I0TEXSyiBnRVUf6D467AQhN8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=iCNYgAblMh/3HoKFHy1PmQhPYg5Asi5nnKIshpg3GBgiki/jHLBmt8ULiNndCeAdH EXhqhweXBfEDY6lincBTQYBrAq4SuCJ+UITCi55BgDovOpMdZRuWEoJxnfm70F/3Qy H18CU9h+hBsAMTfxTy8KNSfkEr8bv0ksYnUJjSG6NFI3KYLwfyj3NpgkJB6SQPAndS eQ6F/S/jV83t3vzeVInzYpWnqvHr5+k9ca/zFGla7fa01E33nC3bp0nKX1EtrPqDsJ dclLI0+qQoRNiX8oIdVlaZXadV6GwWsjchoXzpGVS8VVsPvNSiDMHSPV+Nly7plTpg xjD6ZR683Pb5Q== X-Pm-Submission-Id: 4hMZx31GfBz2SchP From: =?UTF-8?q?Onur=20=C3=96zkan?= To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: dakr@kernel.org, aliceryhl@google.com, daniel.almeida@collabora.com, airlied@gmail.com, simona@ffwll.ch, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v4 3/4] drm/tyr: add GPU reset infrastructure Date: Sat, 15 Aug 2026 13:23:59 +0300 Message-ID: <20260815-tyr-reset-impl-v4-3-578df9a5e576@onurozkan.dev> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> References: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add support for scheduling GPU resets on a dedicated workqueue. Track the reset state to avoid queueing another reset while one is already pending or in progress. Use an SRCU based gate with mutex-protected reader admission to block hardware accesses while reset work runs and wait for current users before resetting. Stop new reset requests during teardown and drain any queued or running reset work before releasing the device resources. This is the initial reset infrastructure only. It is not wired to a reset source yet as those will follow in separate work. Link: https://gitlab.freedesktop.org/panfrost/linux/-/work_items/28 Signed-off-by: Onur =C3=96zkan --- drivers/gpu/drm/tyr/driver.rs | 42 ++---- drivers/gpu/drm/tyr/reset.rs | 260 +++++++++++++++++++++++++++++++= ++++ drivers/gpu/drm/tyr/reset/hw_gate.rs | 80 +++++++++++ drivers/gpu/drm/tyr/tyr.rs | 1 + 4 files changed, 355 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 90d6cd988cd2..bd613ab7e05c 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -8,7 +8,6 @@ device::{ Bound, Core, - Device, DeviceContext, // }, dma::{ @@ -17,13 +16,9 @@ }, drm, drm::ioctl, - io::{ - poll, - Io, // - }, new_mutex, of, - platform, + platform, // prelude::*, regulator, regulator::Regulator, @@ -33,7 +28,6 @@ Arc, Mutex, // }, - time, types::ForLt, // }; =20 @@ -41,10 +35,10 @@ file::TyrDrmFileData, fw::Firmware, gem::BoData, - gpu, gpu::GpuInfo, mmu::Mmu, - regs::gpu_control::*, // + regs::gpu_control::*, + reset, // }; =20 pub(crate) type IoMem<'a> =3D kernel::io::mem::IoMem<'a, SZ_2M>; @@ -67,6 +61,11 @@ pub(crate) struct TyrDrmRegistrationData<'bound> { /// Parent platform device. pub(crate) pdev: &'bound platform::Device, =20 + // `ResetHandle::drop()` drains queued/running works and this must hap= pen + // before clocks/regulators are dropped. So keep this field before the= m to + // ensure the correct drop order. + pub(crate) reset: reset::ResetHandle<'bound>, + /// Firmware sections. pub(crate) fw: Arc>, =20 @@ -85,23 +84,6 @@ pub(crate) struct TyrDrmRegistrationData<'bound> { pub(crate) gpu_info: GpuInfo, } =20 -fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result { - // Clear any stale reset IRQ state before issuing a new soft reset. - iomem.write_reg(GPU_IRQ_CLEAR::zeroed().with_reset_completed(true)); - - iomem.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset)); - - poll::read_poll_timeout( - || Ok(iomem.read(GPU_IRQ_RAWSTAT)), - |status| status.reset_completed(), - time::Delta::from_millis(1), - time::Delta::from_millis(100), - ) - .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?; - - Ok(()) -} - kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, @@ -136,8 +118,7 @@ fn probe<'bound>( =20 let iomem =3D Arc::new(request.iomap_sized::()?, GFP_KERNEL= )?; =20 - issue_soft_reset(pdev.as_ref(), &iomem)?; - gpu::l2_power_on(pdev.as_ref(), &iomem)?; + reset::run_reset(pdev.as_ref(), &iomem)?; =20 let gpu_info =3D GpuInfo::new(&iomem); gpu_info.log(pdev.as_ref()); @@ -152,6 +133,10 @@ fn probe<'bound>( =20 let unreg_dev =3D drm::UnregisteredDevice::::new(pde= v, Ok(()))?; =20 + // SAFETY: `ResetHandle` is stored in registration data created wi= th `new_with_lt` + // and is dropped before the borrowed device and MMIO references e= xpire. + let reset =3D unsafe { reset::ResetHandle::new(pdev, iomem.as_arc_= borrow())? }; + let mmu =3D Mmu::new(iomem.as_arc_borrow(), &gpu_info)?; =20 let firmware =3D Firmware::new( @@ -167,6 +152,7 @@ fn probe<'bound>( =20 let reg_data =3D try_pin_init!(TyrDrmRegistrationData { pdev, + reset, fw: firmware, clks <- new_mutex!(Clocks { core: core_clk, diff --git a/drivers/gpu/drm/tyr/reset.rs b/drivers/gpu/drm/tyr/reset.rs new file mode 100644 index 000000000000..a0eabf8ac6d0 --- /dev/null +++ b/drivers/gpu/drm/tyr/reset.rs @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Provides asynchronous reset handling for the Tyr DRM driver via [`Rese= tHandle`]. +//! +//! [`ResetHandle::schedule`] runs reset work on a dedicated ordered +//! [`ScopedQueue`] and avoids duplicate pending reset requests. +//! +//! # High-level Execution Flow +//! +//! ```text +//! +------+ schedule() +---------+ reset_work() +------------+ +//! | Idle |------------->| Pending |--------------->| InProgress | +//! +------+ +---------+ +------------+ +//! ^ | +//! | work complete | +//! +---------------------------------------------+ +//! +//! Teardown transitions any state to ShuttingDown, then drains pending and +//! running work. +//! ``` + +mod hw_gate; + +use hw_gate::HwGate; + +use kernel::{ + device::{ + Bound, + Device, // + }, + io::{ + poll, + Io, // + }, + platform, + prelude::*, + sync::{ + atomic::{ + Atomic, + AtomicType, + Full, + Release, // + }, + Arc, + ArcBorrow, // + }, + time, + workqueue::{ + self, + ScopedQueue, + Work, // + }, +}; + +use crate::{ + driver::IoMem, + gpu, + regs::gpu_control::*, // +}; + +/// Lifecycle state of the reset worker. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(i32)] +enum ResetState { + /// Hardware is available and no reset request exists. + Idle =3D 0, + /// Reset work item is queued and waiting to be claimed by the worker. + Pending =3D 1, + /// Worker has claimed the request and is resetting hardware. + InProgress =3D 2, + /// Teardown has started and no new reset request may start. + ShuttingDown =3D 3, +} + +// SAFETY: `ResetState` and `i32` have the same size and alignment, and are +// round-trip transmutable. +unsafe impl AtomicType for ResetState { + type Repr =3D i32; +} + +/// Internal reset orchestrator that owns the state, [`HwGate`], and work = item. +#[pin_data] +struct Controller<'bound> { + /// Parent platform device. + pdev: &'bound platform::Device, + /// Mapped register space needed for reset operations. + iomem: Arc>, + /// State shared by reset schedulers and the worker. + state: Atomic, + /// Drains reset-sensitive hardware accesses before a reset. + #[pin] + hw: HwGate, + /// Work item backing async reset processing. + #[pin] + work: Work>, +} + +kernel::impl_has_work! { + impl{'bound} HasWork> for Controller<'bound> { self= .work } +} + +impl<'bound> workqueue::WorkItem for Controller<'bound> { + type Pointer =3D Arc; + + fn run(this: Arc) { + this.reset_work(); + } +} + +impl<'bound> Controller<'bound> { + /// Creates an [`Arc`] ready for use. + fn new( + pdev: &'bound platform::Device, + iomem: ArcBorrow<'_, IoMem<'bound>>, + ) -> Result> { + Arc::pin_init( + try_pin_init!(Self { + pdev, + iomem: iomem.into(), + state: Atomic::new(ResetState::Idle), + hw <- HwGate::new(), + work <- kernel::new_work!("tyr::reset"), + }), + GFP_KERNEL, + ) + } + + /// Attempts to transition the reset state from `from` to `to`. + #[inline] + fn try_transition(&self, from: ResetState, to: ResetState) -> bool { + self.state.cmpxchg(from, to, Full).is_ok() + } + + /// Processes one scheduled reset request. + /// + /// If the pending reset cannot be claimed, the worker returns immedia= tely. + /// + /// It first claims [`ResetState::Pending`], then waits for earlier ha= rdware + /// accesses to complete before issuing the reset and returning the wo= rker + /// state to [`ResetState::Idle`]. + /// + /// Panthor reference: + /// - drivers/gpu/drm/panthor/panthor_device.c::panthor_device_reset_w= ork() + fn reset_work(self: &Arc) { + if !self.try_transition(ResetState::Pending, ResetState::InProgres= s) { + return; + } + + dev_info!(self.pdev, "Starting GPU reset.\n"); + + // Wait for current hardware accesses to finish before resetting. + let reset_guard =3D self.hw.close(); + let reset_result =3D run_reset(self.pdev.as_ref(), &self.iomem); + drop(reset_guard); + + if let Err(e) =3D reset_result { + dev_err!(self.pdev, "GPU reset failed: {:?}\n", e); + + // TODO: Unplug the GPU. + // There is no API for unplugging the GPU and this is unreacha= ble + // for now since there are no hardware users for reset API. + } else { + dev_info!(self.pdev, "GPU reset completed.\n"); + } + + let _ =3D self.try_transition(ResetState::InProgress, ResetState::= Idle); + } +} + +/// User-facing handle for scheduling resets. +/// +/// Dropping the handle drains any queued or in-flight reset work before t= he +/// [`ScopedQueue`] and the clock and regulator resources are released. +pub(crate) struct ResetHandle<'bound> { + controller: Arc>, + wq: ScopedQueue<'bound>, +} + +impl<'bound> ResetHandle<'bound> { + /// Creates [`ResetHandle`]. + /// + /// # Safety + /// + /// The returned handle must not be leaked or otherwise prevented from + /// running [`Drop`], since it owns work that may borrow from `'bound`. + pub(crate) unsafe fn new( + pdev: &'bound platform::Device, + iomem: ArcBorrow<'_, IoMem<'bound>>, + ) -> Result { + Ok(Self { + controller: Controller::new(pdev, iomem)?, + // SAFETY: The caller guarantees the handle is dropped. + wq: unsafe { ScopedQueue::new(c"tyr-reset-wq")? }, + }) + } + + /// Schedules a GPU reset on the dedicated workqueue. + /// + /// If a reset is already pending or in progress the call is a no-op. + #[expect(dead_code)] + pub(crate) fn schedule(&self) { + // TODO: Similar to `panthor_device_schedule_reset()` in Panthor, = add a + // power management check once Tyr supports it. + + if self + .controller + .try_transition(ResetState::Idle, ResetState::Pending) + { + let _ =3D self.wq.enqueue(self.controller.clone()); + } + } +} + +impl<'bound> Drop for ResetHandle<'bound> { + fn drop(&mut self) { + // Stop new reset requests before draining queued/running work. + self.controller + .state + .store(ResetState::ShuttingDown, Release); + + // Not required for safety because `wq` will drain on drop, but ke= ep + // cancellation of `controller.work` explicit before fields are dr= opped. + let _ =3D self.controller.work.cancel_sync(); + } +} + +/// Issues a soft reset command and waits for reset-complete IRQ status. +fn issue_soft_reset<'bound>(dev: &'bound Device, io: &IoMem<'bound>= ) -> Result { + // Clear any stale reset-complete IRQ state before issuing a new soft = reset. + io.write_reg(GPU_IRQ_CLEAR::zeroed().with_reset_completed(true)); + + io.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset)); + + poll::read_poll_timeout( + || Ok(io.read(GPU_IRQ_RAWSTAT)), + |status| status.reset_completed(), + time::Delta::from_millis(1), + time::Delta::from_millis(100), + ) + .inspect_err(|_| dev_err!(dev, "GPU reset timed out."))?; + + Ok(()) +} + +/// Runs one synchronous GPU reset pass. +/// +/// Its visibility is `pub(super)` only so the probe path can run an +/// initial reset; it is not part of this module's public API. +/// +/// On success, the GPU is left in a state suitable for reinitialization. +/// +/// The sequence is as follows: +/// - Trigger a GPU soft reset. +/// - Wait for the reset-complete IRQ status. +/// - Power L2 back on. +pub(super) fn run_reset<'bound>(dev: &'bound Device, iomem: &IoMem<= 'bound>) -> Result { + issue_soft_reset(dev, iomem)?; + gpu::l2_power_on(dev, iomem)?; + Ok(()) +} diff --git a/drivers/gpu/drm/tyr/reset/hw_gate.rs b/drivers/gpu/drm/tyr/res= et/hw_gate.rs new file mode 100644 index 000000000000..54754f9fc05f --- /dev/null +++ b/drivers/gpu/drm/tyr/reset/hw_gate.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Hardware-access gate for the GPU reset cycle. +//! +//! [`HwGate`] uses a mutex and [`Srcu`] to coordinate reset-sensitive har= dware +//! access with reset. Readers hold the mutex while entering SRCU, then re= lease +//! it before accessing hardware. The reset worker holds the mutex while w= aiting +//! for admitted readers and resetting hardware. + +use kernel::{ + prelude::*, + sync::{ + new_mutex, + srcu, + Mutex, + MutexGuard, + Srcu, // + }, +}; + +/// Synchronizes GPU hardware access with reset. +#[pin_data] +pub(super) struct HwGate { + /// Admits readers and is held exclusively while the reset worker owns= the + /// hardware. + #[pin] + gate_lock: Mutex<()>, + /// Drains readers that entered before the reset worker acquired `gate= _lock`. + #[pin] + srcu: Srcu, +} + +impl HwGate { + /// Creates an open hardware-access gate. + pub(super) fn new() -> impl PinInit { + try_pin_init!(Self { + gate_lock <- new_mutex!(()), + srcu <- kernel::new_srcu!(), + }) + } + + /// Enters a reset-sensitive hardware-access section. + #[expect(dead_code)] + fn access(&self) -> HwAccessGuard<'_> { + let gate_lock =3D self.gate_lock.lock(); + let srcu =3D self.srcu.read_lock(); + drop(gate_lock); + + HwAccessGuard { _srcu: srcu } + } + + /// Stops new readers and drains admitted readers for the reset worker. + /// + /// Callers must serialize write-side access. The reset controller's s= tate + /// machine provides that serialization. + pub(super) fn close(&self) -> HwClosedGuard<'_> { + let gate_lock =3D self.gate_lock.lock(); + + // Holding `gate_lock` prevents new readers from entering SRCU. Re= aders + // admitted before us are enrolled, so wait for their read-side wo= rk. + self.srcu.synchronize(); + + HwClosedGuard { + _gate_lock: gate_lock, + } + } +} + +/// Shared hardware access that blocks reset until dropped. +#[must_use =3D "the gate is released when the guard is dropped"] +struct HwAccessGuard<'a> { + _srcu: srcu::Guard<'a>, +} + +/// Exclusive hardware access for the reset worker that blocks new hardware +/// accesses until dropped. +#[must_use =3D "the gate stays closed until the guard is dropped"] +pub(super) struct HwClosedGuard<'a> { + _gate_lock: MutexGuard<'a, ()>, +} diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index 3f6fe5fbeb0f..63873628c843 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -14,6 +14,7 @@ mod gpu; mod mmu; mod regs; +mod reset; mod slot; mod vm; mod wait; --=20 2.51.2 From nobody Mon Sep 28 23:12:41 2026 Received: from mail-43170.protonmail.ch (mail-43170.protonmail.ch [185.70.43.170]) (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 20D913DDB19 for ; Sat, 15 Aug 2026 10:25:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789554; cv=none; b=NL73lGjaOaKIrF+UXz3q4oUJ4/HE5x4VHWxDW5e1LEU9b/dlENhzH0G+0r23wEeeDOG7Yx0ZaLR6hAN7mF53zJi56Vw45QWp+Dez4qdoQyoacRyBinAqRJTFi3HQ0Djn0m+HkbKCCdvxuMY+a/S7dymX9a+HcTOTLAsehFDd2sg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786789554; c=relaxed/simple; bh=5T6caqh6DVd8+QNg9fE2KUurstmfyidbB1GPpOMNZHQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LxOgon+WS8X3kGIpvcFUK8lxu2rBhUykF40pMH0GjnzeVe0XtyXbXrxyYMlWrP4KNAXCO9Qp0DOPLCoWyGd6AzWTqjEJp+sK/wPh95XXNE+28x3Q1NcRaST9zLkRJczmvrA1Z1ScSfCZ/hqgA1cXeF/lWednfcJ0A55H0o97wys= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev; spf=pass smtp.mailfrom=onurozkan.dev; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b=eOcXBfXD; arc=none smtp.client-ip=185.70.43.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=onurozkan.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=onurozkan.dev header.i=@onurozkan.dev header.b="eOcXBfXD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=onurozkan.dev; s=protonmail; t=1786789532; x=1787048732; bh=RZdeoJJVRJ4nWalD/8I8LSa342o9Wo31uEBZe+oaUDg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=eOcXBfXDyDbxom85zs5xd0XIcRnkN2WIcY9aqK9Xf6IhUBgaBQmRlvXSmVi1zLKyv QZ0M7Y0naQ/kQEa/JB467YdHQkZxl3A/x0vtsm5S/QJBOB428iWNboDGPGESZIwiXZ NymUFcCyqokOH9+nxUTkqeLbPTPJ3/WEGilaIW8dRf6KhwzNory6d1KtgUQ1IgEMGb yGAvtHXg47ln5tKKte3/OTn8+2Kawh9HolNvzSye9N4AMjN5oXLZ+31SMgBIISTPaN y9gg0c8c7+u8S8lhnYeYZSwc/7UtxpShsneGMzSu/MYFDKtg6Hs7kaYmBQB6HcpDMv sn1CVFKPj3alg== X-Pm-Submission-Id: 4hMZx60kqkz2SchQ From: =?UTF-8?q?Onur=20=C3=96zkan?= To: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: dakr@kernel.org, aliceryhl@google.com, daniel.almeida@collabora.com, airlied@gmail.com, simona@ffwll.ch, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, =?UTF-8?q?Onur=20=C3=96zkan?= Subject: [PATCH v4 4/4] drm/tyr: put iomem behind the hardware gate Date: Sat, 15 Aug 2026 13:24:00 +0300 Message-ID: <20260815-tyr-reset-impl-v4-4-578df9a5e576@onurozkan.dev> X-Mailer: git-send-email 2.51.2 In-Reply-To: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> References: <20260815-tyr-reset-impl-v4-0-578df9a5e576@onurozkan.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Move iomem mapping into HwGate and pass Arc to components that access hardware. Callers obtain HwAccessGuard before accessing the iomem so the reset worker waits for ongoing accesses. Suggested-by: Daniel Almeida Signed-off-by: Onur =C3=96zkan --- drivers/gpu/drm/tyr/driver.rs | 26 +++++++---------- drivers/gpu/drm/tyr/fw.rs | 16 ++++++----- drivers/gpu/drm/tyr/mmu.rs | 9 ++---- drivers/gpu/drm/tyr/mmu/address_space.rs | 49 ++++++++++++++++------------= ---- drivers/gpu/drm/tyr/reset.rs | 34 +++++++++------------- drivers/gpu/drm/tyr/reset/hw_gate.rs | 40 ++++++++++++++++++++------ 6 files changed, 90 insertions(+), 84 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index bd613ab7e05c..936624340edd 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -75,9 +75,6 @@ pub(crate) struct TyrDrmRegistrationData<'bound> { #[pin] regulators: Mutex, =20 - /// GPU MMIO register mapping. - pub(crate) iomem: Arc>, - /// Some information on the GPU. /// /// This is mainly queried by userspace, i.e.: Mesa. @@ -116,11 +113,15 @@ fn probe<'bound>( =20 let request =3D pdev.io_request_by_index(0).ok_or(ENODEV)?; =20 - let iomem =3D Arc::new(request.iomap_sized::()?, GFP_KERNEL= )?; + let hw =3D Arc::pin_init( + reset::HwGate::new(request.iomap_sized::()?), + GFP_KERNEL, + )?; =20 - reset::run_reset(pdev.as_ref(), &iomem)?; + reset::run_reset(pdev.as_ref(), &hw)?; =20 - let gpu_info =3D GpuInfo::new(&iomem); + let hw_guard =3D hw.access(); + let gpu_info =3D GpuInfo::new(hw_guard.iomem()); gpu_info.log(pdev.as_ref()); =20 let pa_bits =3D MMU_FEATURES::from_raw(gpu_info.mmu_features) @@ -135,17 +136,11 @@ fn probe<'bound>( =20 // SAFETY: `ResetHandle` is stored in registration data created wi= th `new_with_lt` // and is dropped before the borrowed device and MMIO references e= xpire. - let reset =3D unsafe { reset::ResetHandle::new(pdev, iomem.as_arc_= borrow())? }; + let reset =3D unsafe { reset::ResetHandle::new(pdev, hw.clone())? = }; =20 - let mmu =3D Mmu::new(iomem.as_arc_borrow(), &gpu_info)?; + let mmu =3D Mmu::new(hw.clone(), &gpu_info)?; =20 - let firmware =3D Firmware::new( - pdev, - iomem.clone(), - &unreg_dev, - mmu.as_arc_borrow(), - &gpu_info, - )?; + let firmware =3D Firmware::new(pdev, hw.clone(), &unreg_dev, mmu.a= s_arc_borrow(), &gpu_info)?; =20 firmware.boot()?; firmware.enable_global_interface(&gpu_info, &core_clk)?; @@ -163,7 +158,6 @@ fn probe<'bound>( _mali: mali_regulator, _sram: sram_regulator, }), - iomem, gpu_info, }); =20 diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs index 651bbe77f10b..e1522ab14e8d 100644 --- a/drivers/gpu/drm/tyr/fw.rs +++ b/drivers/gpu/drm/tyr/fw.rs @@ -41,7 +41,6 @@ =20 use crate::{ driver::{ - IoMem, TyrDrmDevice, // }, fw::{ @@ -65,6 +64,7 @@ MCU_CONTROL, MCU_STATUS, // }, + reset::HwGate, vm::Vm, // }; =20 @@ -148,8 +148,8 @@ pub(crate) struct Firmware<'bound> { /// Platform device reference (needed to access the MCU JOB_IRQ regist= ers). _pdev: ARef, =20 - /// Iomem need to access registers. - iomem: Arc>, + /// Shared gate that coordinates hardware access with GPU reset. + hw: Arc>, =20 /// MCU VM. vm: Arc>, @@ -221,7 +221,7 @@ fn load( /// Load firmware and map sections into MCU VM. pub(crate) fn new( pdev: &'bound platform::Device, - iomem: Arc>, + hw: Arc>, ddev: &TyrDrmDevice, mmu: ArcBorrow<'_, Mmu<'bound>>, gpu_info: &GpuInfo, @@ -262,7 +262,7 @@ pub(crate) fn new( let firmware =3D Arc::pin_init( try_pin_init!(Firmware { _pdev: pdev.into(), - iomem, + hw, vm, sections, global_iface <- new_mutex!(GlobalInterface::new()?), @@ -288,7 +288,8 @@ pub(crate) fn shared_section<'a>(&'a self) -> Result<&'= a Section<'bound>> { } =20 pub(crate) fn boot(&self) -> Result { - let io =3D &self.iomem; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); io.write_reg(MCU_CONTROL::zeroed().with_req(McuControlMode::Auto)); =20 if let Err(e) =3D poll::read_poll_timeout( @@ -307,8 +308,9 @@ pub(crate) fn boot(&self) -> Result { /// Enable the global interface. pub(crate) fn enable_global_interface(&self, gpu_info: &GpuInfo, core_= clk: &Clk) -> Result { let shared_section =3D self.shared_section()?; + let hw_guard =3D self.hw.access(); self.global_iface .lock() - .enable(&self.iomem, shared_section, gpu_info, core_clk) + .enable(hw_guard.iomem(), shared_section, gpu_info, core_clk) } } diff --git a/drivers/gpu/drm/tyr/mmu.rs b/drivers/gpu/drm/tyr/mmu.rs index cb5908c80e3d..8df6d2ef3c74 100644 --- a/drivers/gpu/drm/tyr/mmu.rs +++ b/drivers/gpu/drm/tyr/mmu.rs @@ -26,7 +26,6 @@ }; =20 use crate::{ - driver::IoMem, gpu::GpuInfo, mmu::address_space::{ AddressSpaceManager, @@ -36,6 +35,7 @@ gpu_control::AS_PRESENT, MAX_AS, // }, + reset::HwGate, slot::SlotManager, // }; =20 @@ -67,14 +67,11 @@ pub(crate) struct Mmu<'bound> { =20 impl<'bound> Mmu<'bound> { /// Create an MMU component for this device. - pub(crate) fn new( - iomem: ArcBorrow<'_, IoMem<'bound>>, - gpu_info: &GpuInfo, - ) -> Result>> { + pub(crate) fn new(hw: Arc>, gpu_info: &GpuInfo) -> Resu= lt>> { let present =3D AS_PRESENT::from_raw(gpu_info.as_present).present(= ).get(); let slot_count =3D present.count_ones().try_into()?; =20 - let as_manager =3D AddressSpaceManager::new(iomem, present)?; + let as_manager =3D AddressSpaceManager::new(hw, present)?; let mmu_init =3D try_pin_init!(Self{ as_manager <- new_mutex!(SlotManager::new(as_manager, slot_cou= nt)?), }); diff --git a/drivers/gpu/drm/tyr/mmu/address_space.rs b/drivers/gpu/drm/tyr= /mmu/address_space.rs index d5274220eb3c..7ce2902e6300 100644 --- a/drivers/gpu/drm/tyr/mmu/address_space.rs +++ b/drivers/gpu/drm/tyr/mmu/address_space.rs @@ -42,7 +42,6 @@ }; =20 use crate::{ - driver::IoMem, mmu::{ AsSlotManager, Mmu, // @@ -52,6 +51,7 @@ mmu_control::mmu_as_control::*, MAX_AS, // }, + reset::HwGate, slot::{ Seat, SlotOperations, // @@ -201,8 +201,8 @@ fn as_config(&self) -> Result { /// /// [`SlotOperations`]: crate::slot::SlotOperations pub(crate) struct AddressSpaceManager<'bound> { - /// Memory-mapped I/O region for GPU register access. - iomem: Arc>, + /// Shared gate that coordinates hardware access with GPU reset. + hw: Arc>, =20 /// Bitmask of available address space slots from GPU_AS_PRESENT regis= ter. as_present: u32, @@ -229,16 +229,13 @@ fn evict(&mut self, slot_idx: usize, _slot_data: &Sel= f::SlotData) -> Result { impl<'bound> AddressSpaceManager<'bound> { /// Creates a new address space manager. /// - /// Initializes the manager with references to the platform device and - /// I/O memory region, along with the bitmask of available AS slots. + /// Initializes the manager with the hardware-access gate and the bitm= ask + /// of available AS slots. pub(super) fn new( - iomem: ArcBorrow<'_, IoMem<'bound>>, + hw: Arc>, as_present: u32, ) -> Result> { - Ok(Self { - iomem: iomem.into(), - as_present, - }) + Ok(Self { hw, as_present }) } =20 /// Validates that an AS slot number is within range and present in ha= rdware. @@ -269,7 +266,8 @@ fn validate_as_slot(&self, as_nr: usize) -> Result { /// /// Returns an error if polling times out after 10ms or if register ac= cess fails. fn as_wait_ready(&self, as_nr: usize) -> Result { - let io =3D &*self.iomem; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); let op =3D || { let status_reg =3D STATUS::try_at(as_nr).ok_or(EINVAL)?; Ok(io.read(status_reg)) @@ -283,9 +281,10 @@ fn as_wait_ready(&self, as_nr: usize) -> Result { /// Sends a command to an AS slot. /// /// Returns an error if waiting for ready times out or if register wri= te fails. - fn as_send_cmd(&mut self, as_nr: usize, cmd: MmuCommand) -> Result { + fn as_send_cmd(&self, as_nr: usize, cmd: MmuCommand) -> Result { self.as_wait_ready(as_nr)?; - let io =3D &*self.iomem; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); let command_reg =3D COMMAND::try_at(as_nr).ok_or(EINVAL)?; io.write(command_reg, COMMAND::zeroed().with_command(cmd)); Ok(()) @@ -294,7 +293,7 @@ fn as_send_cmd(&mut self, as_nr: usize, cmd: MmuCommand= ) -> Result { /// Sends a command to an AS slot and waits for completion. /// /// Returns an error if sending the command fails or if waiting for co= mpletion times out. - fn as_send_cmd_and_wait(&mut self, as_nr: usize, cmd: MmuCommand) -> R= esult { + fn as_send_cmd_and_wait(&self, as_nr: usize, cmd: MmuCommand) -> Resul= t { self.as_send_cmd(as_nr, cmd)?; self.as_wait_ready(as_nr)?; Ok(()) @@ -303,10 +302,10 @@ fn as_send_cmd_and_wait(&mut self, as_nr: usize, cmd:= MmuCommand) -> Result { /// Enables an AS slot with the provided configuration. /// /// Returns an error if the slot is invalid or if register writes/comm= ands fail. - fn as_enable(&mut self, as_nr: usize, as_config: &AddressSpaceConfig) = -> Result { + fn as_enable(&self, as_nr: usize, as_config: &AddressSpaceConfig) -> R= esult { self.validate_as_slot(as_nr)?; - - let io =3D &*self.iomem; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); =20 let transtab =3D as_config.transtab; io.write( @@ -346,14 +345,14 @@ fn as_enable(&mut self, as_nr: usize, as_config: &Add= ressSpaceConfig) -> Result /// Disables an AS slot and clears its configuration. /// /// Returns an error if the slot is invalid or if register writes/comm= ands fail. - fn as_disable(&mut self, as_nr: usize) -> Result { + fn as_disable(&self, as_nr: usize) -> Result { self.validate_as_slot(as_nr)?; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); =20 // Flush AS before disabling self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushMem)?; =20 - let io =3D &*self.iomem; - io.write( TRANSTAB_LO::try_at(as_nr).ok_or(EINVAL)?, TRANSTAB_LO::from_raw(0), @@ -397,8 +396,10 @@ fn as_disable(&mut self, as_nr: usize) -> Result { /// power-of-two region aligned to its size. /// /// Returns an error if the slot is invalid or if register writes/comm= ands fail. - fn as_start_update(&mut self, as_nr: usize, region: &Range) -> Re= sult { + fn as_start_update(&self, as_nr: usize, region: &Range) -> Result= { self.validate_as_slot(as_nr)?; + let hw_guard =3D self.hw.access(); + let io =3D hw_guard.iomem(); =20 // The lock operates on full 64-byte cache lines of translation ta= ble entries. // Since each translation table entry (TTE) is 8 bytes, a cache li= ne has 8 TTEs. @@ -436,8 +437,6 @@ fn as_start_update(&mut self, as_nr: usize, region: &Ra= nge) -> Result { // because log2(32 KiB) =3D 15. let lockaddr_size =3D lock_region_log2 - 1; =20 - let io =3D &*self.iomem; - let lockaddr_val =3D LOCKADDR::zeroed() .try_with_size(lockaddr_size)? .try_with_base(lockaddr_base)? @@ -458,7 +457,7 @@ fn as_start_update(&mut self, as_nr: usize, region: &Ra= nge) -> Result { /// Completes an atomic translation table update. /// /// Returns an error if the slot is invalid or if the flush command fa= ils. - fn as_end_update(&mut self, as_nr: usize) -> Result { + fn as_end_update(&self, as_nr: usize) -> Result { self.validate_as_slot(as_nr)?; self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushPt)?; Ok(()) @@ -467,7 +466,7 @@ fn as_end_update(&mut self, as_nr: usize) -> Result { /// Flushes the translation table cache for an AS slot. /// /// Returns an error if the slot is invalid or if the flush command fa= ils. - fn as_flush(&mut self, as_nr: usize) -> Result { + fn as_flush(&self, as_nr: usize) -> Result { self.validate_as_slot(as_nr)?; self.as_send_cmd(as_nr, MmuCommand::FlushPt) } diff --git a/drivers/gpu/drm/tyr/reset.rs b/drivers/gpu/drm/tyr/reset.rs index a0eabf8ac6d0..69c1ea3d6abc 100644 --- a/drivers/gpu/drm/tyr/reset.rs +++ b/drivers/gpu/drm/tyr/reset.rs @@ -21,7 +21,7 @@ =20 mod hw_gate; =20 -use hw_gate::HwGate; +pub(crate) use hw_gate::HwGate; =20 use kernel::{ device::{ @@ -41,8 +41,7 @@ Full, Release, // }, - Arc, - ArcBorrow, // + Arc, // }, time, workqueue::{ @@ -83,13 +82,10 @@ unsafe impl AtomicType for ResetState { struct Controller<'bound> { /// Parent platform device. pdev: &'bound platform::Device, - /// Mapped register space needed for reset operations. - iomem: Arc>, /// State shared by reset schedulers and the worker. state: Atomic, - /// Drains reset-sensitive hardware accesses before a reset. - #[pin] - hw: HwGate, + /// Shared gate that coordinates hardware access with GPU reset. + hw: Arc>, /// Work item backing async reset processing. #[pin] work: Work>, @@ -109,16 +105,12 @@ fn run(this: Arc) { =20 impl<'bound> Controller<'bound> { /// Creates an [`Arc`] ready for use. - fn new( - pdev: &'bound platform::Device, - iomem: ArcBorrow<'_, IoMem<'bound>>, - ) -> Result> { + fn new(pdev: &'bound platform::Device, hw: Arc>)= -> Result> { Arc::pin_init( try_pin_init!(Self { pdev, - iomem: iomem.into(), state: Atomic::new(ResetState::Idle), - hw <- HwGate::new(), + hw, work <- kernel::new_work!("tyr::reset"), }), GFP_KERNEL, @@ -148,10 +140,7 @@ fn reset_work(self: &Arc) { =20 dev_info!(self.pdev, "Starting GPU reset.\n"); =20 - // Wait for current hardware accesses to finish before resetting. - let reset_guard =3D self.hw.close(); - let reset_result =3D run_reset(self.pdev.as_ref(), &self.iomem); - drop(reset_guard); + let reset_result =3D run_reset(self.pdev.as_ref(), &self.hw); =20 if let Err(e) =3D reset_result { dev_err!(self.pdev, "GPU reset failed: {:?}\n", e); @@ -185,10 +174,10 @@ impl<'bound> ResetHandle<'bound> { /// running [`Drop`], since it owns work that may borrow from `'bound`. pub(crate) unsafe fn new( pdev: &'bound platform::Device, - iomem: ArcBorrow<'_, IoMem<'bound>>, + hw: Arc>, ) -> Result { Ok(Self { - controller: Controller::new(pdev, iomem)?, + controller: Controller::new(pdev, hw)?, // SAFETY: The caller guarantees the handle is dropped. wq: unsafe { ScopedQueue::new(c"tyr-reset-wq")? }, }) @@ -253,7 +242,10 @@ fn issue_soft_reset<'bound>(dev: &'bound Device= , io: &IoMem<'bound>) -> R /// - Trigger a GPU soft reset. /// - Wait for the reset-complete IRQ status. /// - Power L2 back on. -pub(super) fn run_reset<'bound>(dev: &'bound Device, iomem: &IoMem<= 'bound>) -> Result { +pub(super) fn run_reset<'bound>(dev: &'bound Device, hw: &HwGate<'b= ound>) -> Result { + let hw_guard =3D hw.close(); + let iomem =3D hw_guard.iomem(); + issue_soft_reset(dev, iomem)?; gpu::l2_power_on(dev, iomem)?; Ok(()) diff --git a/drivers/gpu/drm/tyr/reset/hw_gate.rs b/drivers/gpu/drm/tyr/res= et/hw_gate.rs index 54754f9fc05f..e761a0b03661 100644 --- a/drivers/gpu/drm/tyr/reset/hw_gate.rs +++ b/drivers/gpu/drm/tyr/reset/hw_gate.rs @@ -18,9 +18,13 @@ }, }; =20 +use crate::driver::IoMem; + /// Synchronizes GPU hardware access with reset. #[pin_data] -pub(super) struct HwGate { +pub(crate) struct HwGate<'bound> { + /// GPU MMIO register mapping. + iomem: IoMem<'bound>, /// Admits readers and is held exclusively while the reset worker owns= the /// hardware. #[pin] @@ -30,30 +34,33 @@ pub(super) struct HwGate { srcu: Srcu, } =20 -impl HwGate { +impl<'bound> HwGate<'bound> { /// Creates an open hardware-access gate. - pub(super) fn new() -> impl PinInit { + pub(crate) fn new(iomem: IoMem<'bound>) -> impl PinInit { try_pin_init!(Self { + iomem, gate_lock <- new_mutex!(()), srcu <- kernel::new_srcu!(), }) } =20 /// Enters a reset-sensitive hardware-access section. - #[expect(dead_code)] - fn access(&self) -> HwAccessGuard<'_> { + pub(crate) fn access(&self) -> HwAccessGuard<'_, 'bound> { let gate_lock =3D self.gate_lock.lock(); let srcu =3D self.srcu.read_lock(); drop(gate_lock); =20 - HwAccessGuard { _srcu: srcu } + HwAccessGuard { + gate: self, + _srcu: srcu, + } } =20 /// Stops new readers and drains admitted readers for the reset worker. /// /// Callers must serialize write-side access. The reset controller's s= tate /// machine provides that serialization. - pub(super) fn close(&self) -> HwClosedGuard<'_> { + pub(super) fn close(&self) -> HwClosedGuard<'_, 'bound> { let gate_lock =3D self.gate_lock.lock(); =20 // Holding `gate_lock` prevents new readers from entering SRCU. Re= aders @@ -61,6 +68,7 @@ pub(super) fn close(&self) -> HwClosedGuard<'_> { self.srcu.synchronize(); =20 HwClosedGuard { + gate: self, _gate_lock: gate_lock, } } @@ -68,13 +76,27 @@ pub(super) fn close(&self) -> HwClosedGuard<'_> { =20 /// Shared hardware access that blocks reset until dropped. #[must_use =3D "the gate is released when the guard is dropped"] -struct HwAccessGuard<'a> { +pub(crate) struct HwAccessGuard<'a, 'bound> { + gate: &'a HwGate<'bound>, _srcu: srcu::Guard<'a>, } =20 +impl<'a, 'bound> HwAccessGuard<'a, 'bound> { + pub(crate) fn iomem(&self) -> &IoMem<'bound> { + &self.gate.iomem + } +} + /// Exclusive hardware access for the reset worker that blocks new hardware /// accesses until dropped. #[must_use =3D "the gate stays closed until the guard is dropped"] -pub(super) struct HwClosedGuard<'a> { +pub(super) struct HwClosedGuard<'a, 'bound> { + gate: &'a HwGate<'bound>, _gate_lock: MutexGuard<'a, ()>, } + +impl<'a, 'bound> HwClosedGuard<'a, 'bound> { + pub(super) fn iomem(&self) -> &IoMem<'bound> { + &self.gate.iomem + } +} --=20 2.51.2