diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index 7ca3f2329e6a..105a1b727a08 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -50,6 +50,51 @@ 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.
+pub trait PhyContext: private::Sealed {}
+
+/// The [`Device`] context of every [`Driver`] callback that does not take a
+/// [`Device<MaybeLocked>`].
+///
+/// PHYLIB calls these callbacks in one of three ways:
+///
+/// - With `phy_device->lock` held: every callback not listed below.
+/// - Without the lock, but where PHYLIB ensures by other means that no other path accesses the
+/// instance: [`Driver::probe`] and [`Driver::get_features`], which are called from
+/// `phy_probe()`, and [`Driver::suspend`] and [`Driver::resume`].
+/// - Without the lock, possibly on an unbound device or one bound to another driver:
+/// [`Driver::match_phy_device`], which therefore only gets a shared reference.
+///
+/// # Invariants
+///
+/// For the lifetime of a `&mut Device<Locked>`, no other thread accesses the state of the
+/// `phy_device` that `phy_device->lock` protects.
+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,6 +102,9 @@ 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 the exclusive access that
+/// [`Locked`] describes are implemented for [`Device<Locked>`] only.
+///
/// # Invariants
///
/// - Referencing a `phy_device` using this struct asserts that you are in@@ -71,34 +119,9 @@ 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.
- ///
- /// # Safety
- ///
- /// For the duration of `'a`,
- /// - the pointer must point at a valid `phy_device`, and the caller
- /// must be in a context where all methods defined on this struct
- /// are safe to call.
- /// - `(*ptr).mdio.dev` must be a valid.
- unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
- // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
- let ptr = ptr.cast::<Self>();
- // SAFETY: by the function requirements the pointer is valid and we have unique access for
- // the duration of `'a`.
- 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,17 +200,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();@@ -206,14 +218,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 +280,52 @@ pub fn genphy_read_abilities(&mut self) -> Result {
}
}
-impl AsRef<kernel::device::Device> for Device {
+impl<Ctx: PhyContext> Device<Ctx> {
+ /// Creates a new [`Device`] instance from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// For the duration of `'a`,
+ /// - the pointer must point at a valid `phy_device`, and the caller
+ /// must be in the `Ctx` context, see [`Locked`] and [`MaybeLocked`].
+ /// - `(*ptr).mdio.dev` must be a valid.
+ unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
+ // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
+ let ptr = ptr.cast::<Self>();
+ // SAFETY: By the function requirements the pointer is valid for `'a`. `Opaque` permits
+ // other `&mut` to the same `phy_device`, e.g. in a concurrent `MaybeLocked` callback.
+ unsafe { &mut *ptr }
+ }
+
+ /// 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 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)
+ }
+
+ /// 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`. The C
+ // function does not require `phy_device->lock`; PHYLIB itself calls it without the lock.
+ to_result(unsafe { bindings::genphy_soft_reset(phydev) })
+ }
+}
+
+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..e6892f3b855f 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: &mut Device<C>) -> Result<u16>;
/// Writes a PHY register.
- fn write(&self, dev: &mut Device, val: u16) -> Result;
+ fn write<C: PhyContext>(&self, dev: &mut 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: &mut 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: &mut 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: &mut 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: &mut 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