diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index c4e7b1d6c6f4..60c58bbc12d6 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -50,6 +50,48 @@ pub enum DuplexMode {
Unknown,
}
+/// A trait implemented by all contexts a [`Device`] can be used in.
+///
+/// - [`Locked`]: Serialized against every access made under `phy_device->lock`. The default
+/// context of [`Device`].
+/// - [`MaybeLocked`]: `phy_device->lock` may or may not be held.
+///
+/// [`Device<Locked>`] dereferences to [`Device<MaybeLocked>`].
+pub trait PhyContext: private::Sealed {}
+
+/// The [`Device`] context of every [`Driver`] callback that does not take a
+/// [`Device<MaybeLocked>`].
+///
+/// PHYLIB holds `phy_device->lock` for these callbacks, except for [`Driver::probe`], which runs
+/// before the device is published, and for [`Driver::suspend`] and [`Driver::resume`], which run
+/// without concurrent access to the instance.
+///
+/// # Invariants
+///
+/// For the lifetime of a [`Device<Locked>`] reference, the holder is serialized against every
+/// other access to the `phy_device` made under `phy_device->lock`.
+pub struct Locked;
+
+/// The [`Device`] context of callbacks that PHYLIB calls both with and without
+/// `phy_device->lock` held.
+///
+/// A callback in this context must not take `phy_device->lock`.
+///
+/// # Invariants
+///
+/// None beyond those of [`Device`].
+pub struct MaybeLocked;
+
+mod private {
+ pub trait Sealed {}
+
+ impl Sealed for super::Locked {}
+ impl Sealed for super::MaybeLocked {}
+}
+
+impl PhyContext for Locked {}
+impl PhyContext for MaybeLocked {}
+
/// An instance of a PHY device.
///
/// Wraps the kernel's [`struct phy_device`].@@ -57,11 +99,13 @@ pub enum DuplexMode {
/// A [`Device`] instance is created when a callback in [`Driver`] is executed. A PHY driver
/// executes [`Driver`]'s methods during the callback.
///
+/// `Ctx` is the [`PhyContext`] of the callback. Methods that rely on `phy_device->lock` are
+/// implemented for [`Device<Locked>`] only.
+///
/// # Invariants
///
-/// - Referencing a `phy_device` using this struct asserts that you are in
-/// a context where all methods defined on this struct are safe to call.
-/// - This struct always has a valid `self.0.mdio.dev`.
+/// - `self.0` is a valid `phy_device`, and `self.0.mdio.dev` is a valid `struct device`.
+/// - The invariants of `Ctx` hold for the lifetime of a reference to [`Device<Ctx>`].
///
/// [`struct phy_device`]: srctree/include/linux/phy.h
// During the calls to most functions in [`Driver`], the C side (`PHYLIB`) holds a lock that is@@ -71,7 +115,7 @@ pub enum DuplexMode {
// [`Driver::resume`] and [`Driver::suspend`] also are called where only one thread can access
// to the instance.
#[repr(transparent)]
-pub struct Device(Opaque<bindings::phy_device>);
+pub struct Device<Ctx: PhyContext = Locked>(Opaque<bindings::phy_device>, PhantomData<Ctx>);
impl Device {
/// Creates a new [`Device`] instance from a raw pointer.@@ -91,14 +135,6 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
unsafe { &mut *ptr }
}
- /// Gets the id of the PHY.
- pub fn phy_id(&self) -> u32 {
- let phydev = self.0.get();
- // SAFETY: The struct invariant ensures that we may access
- // this field without additional synchronization.
- unsafe { (*phydev).phy_id }
- }
-
/// Gets the state of PHY state machine states.
pub fn state(&self) -> DeviceState {
let phydev = self.0.get();@@ -177,27 +213,6 @@ pub fn set_duplex(&mut self, mode: DuplexMode) {
unsafe { (*phydev).duplex = v as c_int };
}
- /// Reads a PHY register.
- // This function reads a hardware register and updates the stats so takes `&mut self`.
- pub fn read<R: reg::Register>(&mut self, reg: R) -> Result<u16> {
- reg.read(self)
- }
-
- /// Writes a PHY register.
- pub fn write<R: reg::Register>(&mut self, reg: R, val: u16) -> Result {
- reg.write(self, val)
- }
-
- /// Reads a paged register.
- pub fn read_paged(&mut self, page: u16, regnum: u16) -> Result<u16> {
- let phydev = self.0.get();
- // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
- // So it's just an FFI call.
- let ret = unsafe { bindings::phy_read_paged(phydev, page.into(), regnum.into()) };
-
- to_result(ret).map(|()| ret as u16)
- }
-
/// Resolves the advertisements into PHY settings.
pub fn resolve_aneg_linkmode(&mut self) {
let phydev = self.0.get();@@ -206,14 +221,6 @@ pub fn resolve_aneg_linkmode(&mut self) {
unsafe { bindings::phy_resolve_aneg_linkmode(phydev) };
}
- /// Executes software reset the PHY via `BMCR_RESET` bit.
- pub fn genphy_soft_reset(&mut self) -> Result {
- let phydev = self.0.get();
- // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
- // So it's just an FFI call.
- to_result(unsafe { bindings::genphy_soft_reset(phydev) })
- }
-
/// Initializes the PHY.
pub fn init_hw(&mut self) -> Result {
let phydev = self.0.get();@@ -276,7 +283,61 @@ pub fn genphy_read_abilities(&mut self) -> Result {
}
}
-impl AsRef<kernel::device::Device> for Device {
+impl<Ctx: PhyContext> Device<Ctx> {
+ /// Gets the id of the PHY.
+ pub fn phy_id(&self) -> u32 {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is valid by the type invariant of `Self`. `phy_id` is set by
+ // `phy_device_create()` and never written afterwards.
+ unsafe { (*phydev).phy_id }
+ }
+
+ /// Reads a PHY register.
+ // This function reads a hardware register and updates the stats.
+ pub fn read<R: reg::Register>(&self, reg: R) -> Result<u16> {
+ reg.read(self)
+ }
+
+ /// Writes a PHY register.
+ pub fn write<R: reg::Register>(&self, reg: R, val: u16) -> Result {
+ reg.write(self, val)
+ }
+
+ /// Reads a paged register.
+ pub fn read_paged(&self, page: u16, regnum: u16) -> Result<u16> {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So it's just an FFI call.
+ let ret = unsafe { bindings::phy_read_paged(phydev, page.into(), regnum.into()) };
+
+ to_result(ret).map(|()| ret as u16)
+ }
+
+ /// Executes software reset the PHY via `BMCR_RESET` bit.
+ pub fn genphy_soft_reset(&self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So it's just an FFI call.
+ to_result(unsafe { bindings::genphy_soft_reset(phydev) })
+ }
+}
+
+impl core::ops::Deref for Device<Locked> {
+ type Target = Device<MaybeLocked>;
+
+ fn deref(&self) -> &Self::Target {
+ let ptr: *const Self = self;
+
+ // CAST: `Device<Locked>` and `Device<MaybeLocked>` transparently wrap the same type.
+ let ptr = ptr.cast::<Self::Target>();
+
+ // SAFETY: `ptr` was derived from `&self`, and the invariants of `MaybeLocked` are
+ // implied by those of `Locked`.
+ unsafe { &*ptr }
+ }
+}
+
+impl<Ctx: PhyContext> AsRef<kernel::device::Device> for Device<Ctx> {
fn as_ref(&self) -> &kernel::device::Device {
let phydev = self.0.get();
// SAFETY: The struct invariant ensures that `mdio.dev` is valid.diff --git a/rust/kernel/net/phy/reg.rs b/rust/kernel/net/phy/reg.rs
index 80e22c264ea8..6d27a70d6180 100644
--- a/rust/kernel/net/phy/reg.rs
+++ b/rust/kernel/net/phy/reg.rs
@@ -8,7 +8,10 @@
//! Ethernet management interface clauses 22 and 45 register namespaces, as
//! defined in IEEE 802.3.
-use super::Device;
+use super::{
+ Device,
+ PhyContext, //
+};
use crate::{
build_assert::build_assert,
error::*,@@ -44,10 +47,10 @@ pub trait Sealed {}
/// ```
pub trait Register: private::Sealed {
/// Reads a PHY register.
- fn read(&self, dev: &mut Device) -> Result<u16>;
+ fn read<C: PhyContext>(&self, dev: &Device<C>) -> Result<u16>;
/// Writes a PHY register.
- fn write(&self, dev: &mut Device, val: u16) -> Result;
+ fn write<C: PhyContext>(&self, dev: &Device<C>, val: u16) -> Result;
/// Checks the link status and updates current link state.
fn read_status(dev: &mut Device) -> Result<u16>;@@ -104,7 +107,7 @@ pub const fn vendor_specific<const N: u8>() -> Self {
impl private::Sealed for C22 {}
impl Register for C22 {
- fn read(&self, dev: &mut Device) -> Result<u16> {
+ fn read<C: PhyContext>(&self, dev: &Device<C>) -> Result<u16> {
let phydev = dev.0.get();
// SAFETY: `phydev` is pointing to a valid object by the type invariant of `Device`.
// So it's just an FFI call, open code of `phy_read()` with a valid `phy_device` pointer@@ -116,7 +119,7 @@ fn read(&self, dev: &mut Device) -> Result<u16> {
Ok(ret as u16)
}
- fn write(&self, dev: &mut Device, val: u16) -> Result {
+ fn write<C: PhyContext>(&self, dev: &Device<C>, val: u16) -> Result {
let phydev = dev.0.get();
// SAFETY: `phydev` is pointing to a valid object by the type invariant of `Device`.
// So it's just an FFI call, open code of `phy_write()` with a valid `phy_device` pointer@@ -196,7 +199,7 @@ pub fn new(devad: Mmd, regnum: u16) -> Self {
impl private::Sealed for C45 {}
impl Register for C45 {
- fn read(&self, dev: &mut Device) -> Result<u16> {
+ fn read<C: PhyContext>(&self, dev: &Device<C>) -> Result<u16> {
let phydev = dev.0.get();
// SAFETY: `phydev` is pointing to a valid object by the type invariant of `Device`.
// So it's just an FFI call.@@ -206,7 +209,7 @@ fn read(&self, dev: &mut Device) -> Result<u16> {
Ok(ret as u16)
}
- fn write(&self, dev: &mut Device, val: u16) -> Result {
+ fn write<C: PhyContext>(&self, dev: &Device<C>, val: u16) -> Result {
let phydev = dev.0.get();
// SAFETY: `phydev` is pointing to a valid object by the type invariant of `Device`.
// So it's just an FFI call.
--
2.54.0