public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Artem Lytkin <iprintercanon@gmail.com>
To: netdev@vger.kernel.org, rust-for-linux@vger.kernel.org
Cc: fujita.tomonori@gmail.com, andrew@lunn.ch, hkallweit1@gmail.com,
	tmgross@umich.edu, ojeda@kernel.org, dakr@kernel.org
Subject: [PATCH net-next v2 4/5] rust: phy: add interrupt support
Date: Tue, 24 Mar 2026 18:52:48 +0300	[thread overview]
Message-ID: <20260324155249.15098-5-iprintercanon@gmail.com> (raw)
In-Reply-To: <20260324155249.15098-1-iprintercanon@gmail.com>

Add interrupt handling support to the Rust PHY abstraction:

New driver callbacks:
  - config_intr: Enables or disables PHY link state change interrupts.
  - handle_interrupt: Called when the PHY's interrupt fires. Returns
    IrqReturn (reused from kernel::irq). Unlike other callbacks that
    use from_result(), this directly returns irqreturn_t matching the
    C callback signature.

New Device methods:
  - trigger_machine(): Schedules a PHY state machine update. Used in
    interrupt handlers after detecting a link change event.
  - phy_error(): Reports a PHY error and transitions the state machine
    to the error state. Produces a WARN_ON. Must be called with
    phy_device->lock held (which is the case inside handle_interrupt).

Both callbacks run with phy_device->lock held (from phy_interrupt()
in the threaded IRQ context), consistent with the existing callback
safety model.

These additions enable Rust PHY drivers to use interrupt-driven link
detection, which is required for production-quality drivers like the
Realtek RTL8211F.

Signed-off-by: Artem Lytkin <iprintercanon@gmail.com>
---
 rust/kernel/net/phy.rs | 93 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 90 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index 9ffece7ee41c7..e8f920620841c 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -6,7 +6,7 @@
 //!
 //! C headers: [`include/linux/phy.h`](srctree/include/linux/phy.h).
 
-use crate::{device_id::RawDeviceId, error::*, prelude::*, types::Opaque};
+use crate::{device_id::RawDeviceId, error::*, irq::IrqReturn, prelude::*, types::Opaque};
 use core::{marker::PhantomData, ptr::addr_of_mut};
 
 pub mod reg;
@@ -211,6 +211,20 @@ pub fn interface(&self) -> u32 {
         unsafe { (*phydev).interface }
     }
 
+    /// Returns `true` if PHY interrupts are enabled.
+    ///
+    /// This reflects the `phydev->interrupts` field which is set by the PHY
+    /// core before calling [`Driver::config_intr`] to indicate whether
+    /// interrupts should be enabled or disabled.
+    pub fn is_interrupts_enabled(&self) -> bool {
+        // TODO: the code to access the bit field will be replaced with automatically
+        // generated code by bindgen when it becomes possible.
+        // SAFETY: The struct invariant ensures that we may access
+        // this field without additional synchronization.
+        let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
+        bit_field.get(18, 1) == 1
+    }
+
     /// Gets the PHY's IRQ number.
     pub fn irq(&self) -> i32 {
         let phydev = self.0.get();
@@ -358,6 +372,29 @@ pub fn genphy_read_abilities(&mut self) -> Result {
         // So it's just an FFI call.
         to_result(unsafe { bindings::genphy_read_abilities(phydev) })
     }
+
+    /// Triggers the PHY state machine to run.
+    ///
+    /// Used in interrupt handlers to schedule a state machine update
+    /// after processing an interrupt event.
+    pub fn trigger_machine(&mut self) {
+        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.
+        unsafe { bindings::phy_trigger_machine(phydev) };
+    }
+
+    /// Reports a PHY error and moves the state machine to the error state.
+    ///
+    /// Used in interrupt handlers when a register read fails. This will
+    /// produce a kernel `WARN_ON`. Must be called with `phy_device->lock`
+    /// held (which is the case inside [`Driver::handle_interrupt`]).
+    pub fn phy_error(&mut self) {
+        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.
+        unsafe { bindings::phy_error(phydev) };
+    }
 }
 
 impl AsRef<kernel::device::Device> for Device {
@@ -393,8 +430,9 @@ impl<T: Driver> Adapter<T> {
     /// `phydev` must be passed by the corresponding callback in `phy_driver`.
     unsafe extern "C" fn config_init_callback(phydev: *mut bindings::phy_device) -> c_int {
         from_result(|| {
-            // SAFETY: The C core calls config_init with the PHY mutex held
-            // (from phy_init_hw), so the accessors on `Device` are okay to call.
+            // SAFETY: This callback is called only in contexts
+            // where we hold `phy_device->lock`, so the accessors on
+            // `Device` are okay to call.
             let dev = unsafe { Device::from_raw(phydev) };
             T::config_init(dev)?;
             Ok(0)
@@ -507,6 +545,30 @@ impl<T: Driver> Adapter<T> {
     /// # Safety
     ///
     /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+    unsafe extern "C" fn config_intr_callback(phydev: *mut bindings::phy_device) -> c_int {
+        from_result(|| {
+            // SAFETY: This callback is called only in contexts
+            // where we hold `phy_device->lock`, so the accessors on
+            // `Device` are okay to call.
+            let dev = unsafe { Device::from_raw(phydev) };
+            T::config_intr(dev)?;
+            Ok(0)
+        })
+    }
+
+    /// # Safety
+    ///
+    /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+    unsafe extern "C" fn handle_interrupt_callback(
+        phydev: *mut bindings::phy_device,
+    ) -> bindings::irqreturn_t {
+        // SAFETY: This callback is called only in contexts
+        // where we hold `phy_device->lock` (from phy_interrupt),
+        // so the accessors on `Device` are okay to call.
+        let dev = unsafe { Device::from_raw(phydev) };
+        T::handle_interrupt(dev) as core::ffi::c_uint
+    }
+
     unsafe extern "C" fn config_aneg_callback(phydev: *mut bindings::phy_device) -> c_int {
         from_result(|| {
             // SAFETY: This callback is called only in contexts
@@ -660,6 +722,16 @@ pub const fn create_phy_driver<T: Driver>() -> DriverVTable {
         } else {
             None
         },
+        config_intr: if T::HAS_CONFIG_INTR {
+            Some(Adapter::<T>::config_intr_callback)
+        } else {
+            None
+        },
+        handle_interrupt: if T::HAS_HANDLE_INTERRUPT {
+            Some(Adapter::<T>::handle_interrupt_callback)
+        } else {
+            None
+        },
         config_aneg: if T::HAS_CONFIG_ANEG {
             Some(Adapter::<T>::config_aneg_callback)
         } else {
@@ -743,6 +815,21 @@ fn match_phy_device(_dev: &Device) -> bool {
         false
     }
 
+    /// Enables or disables PHY interrupts.
+    fn config_intr(_dev: &mut Device) -> Result {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Handles a PHY interrupt.
+    ///
+    /// Called when the PHY's interrupt line fires. The driver should read the
+    /// interrupt status register to determine the cause and call
+    /// [`Device::trigger_machine`] to schedule a state machine update.
+    /// Returns [`IrqReturn::Handled`] if the interrupt was from this PHY.
+    fn handle_interrupt(_dev: &mut Device) -> IrqReturn {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
     /// Configures the advertisement and resets auto-negotiation
     /// if auto-negotiation is enabled.
     fn config_aneg(_dev: &mut Device) -> Result {
-- 
2.43.0


  parent reply	other threads:[~2026-03-24 15:53 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-24 15:52 [PATCH net-next v2 0/5] rust: phy: extend abstractions for real-world PHY drivers Artem Lytkin
2026-03-24 15:52 ` [PATCH net-next v2 1/5] rust: phy: add read-only device field accessors Artem Lytkin
2026-03-24 15:52 ` [PATCH net-next v2 2/5] rust: phy: add paged register access and bit manipulation helpers Artem Lytkin
2026-03-24 15:52 ` [PATCH net-next v2 3/5] rust: phy: add config_init, read_page, and write_page callbacks Artem Lytkin
2026-03-24 15:52 ` Artem Lytkin [this message]
2026-03-24 15:52 ` [PATCH net-next v2 5/5] net: phy: realtek: add Rust RTL8211F PHY driver Artem Lytkin
2026-03-24 15:57   ` Andrew Lunn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260324155249.15098-5-iprintercanon@gmail.com \
    --to=iprintercanon@gmail.com \
    --cc=andrew@lunn.ch \
    --cc=dakr@kernel.org \
    --cc=fujita.tomonori@gmail.com \
    --cc=hkallweit1@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox