public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
@ 2026-03-10 15:19 Muchamad Coirul Anwar
  2026-03-10 15:52 ` Andrew Lunn
  0 siblings, 1 reply; 5+ messages in thread
From: Muchamad Coirul Anwar @ 2026-03-10 15:19 UTC (permalink / raw)
  To: andrew, hkallweit1, ojeda
  Cc: linux, davem, edumazet, kuba, pabeni, netdev, rust-for-linux,
	linux-kernel, Muchamad Coirul Anwar

This is an experimental Rust port of the legacy Davicom PHY driver
(davicom.c) to explore the boundaries of the current PHY abstractions.

During the porting process, a few limitations in the current
`net::phy::Driver` trait were observed:
1. Callbacks for `config_init`, `config_intr`, and `handle_interrupt`
   are not yet exposed.
2. `bindings::genphy_config_aneg` is not yet wrapped.

In this RFC, the logic for these missing callbacks is implemented
and marked with `#[allow(dead_code)]` to demonstrate the required
hardware logic. Additionally, `unsafe` blocks are used as a temporary
workaround for `genphy_config_aneg` and interface checking.

Note: I don't have access to the physical Davicom hardware.
This patch is compile-tested and verified via QEMU only. It is
submitted as an RFC to share findings regarding the missing
abstractions and gather feedback on the Rust PHY usage.

Signed-off-by: Muchamad Coirul Anwar  <muchamadcoirulanwar@gmail.com>
---
 drivers/net/phy/davicom_rust.rs | 165 ++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100644 drivers/net/phy/davicom_rust.rs

diff --git a/drivers/net/phy/davicom_rust.rs b/drivers/net/phy/davicom_rust.rs
new file mode 100644
index 000000000000..173f14ce25cf
--- /dev/null
+++ b/drivers/net/phy/davicom_rust.rs
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Davicom PHY Rust driver.
+//!
+//! C version `drivers/net/phy/davicom.c`
+
+use kernel::net::phy::{self, reg::C22, DeviceId, Driver};
+use kernel::prelude::*;
+
+/// Register 0x10: Scrambler Control Register (SCR)
+const SCR: C22 = C22::vendor_specific::<0x10>();
+const SCR_INIT: u16 = 0x0610;
+const SCR_RMII: u16 = 0x0100;
+
+/* DM9161 Interrupt Register */
+
+/// Register 0x15: Interrupt Register
+const INTR: C22 = C22::vendor_specific::<0x15>();
+const INTR_PEND: u16 = 0x8000;
+const INTR_DPLX_MASK: u16 = 0x0800;
+const INTR_SPD_MASK: u16 = 0x0400;
+const INTR_LINK_MASK: u16 = 0x0200;
+const INTR_MASK: u16 = 0x0100;
+const INTR_DPLX_CHANGE: u16 = 0x0010;
+const INTR_SPD_CHANGE: u16 = 0x0008;
+const INTR_LINK_CHANGE: u16 = 0x0004;
+const INTR_INIT: u16 = 0x0000;
+const INTR_STOP: u16 = INTR_DPLX_MASK | INTR_SPD_MASK | INTR_LINK_MASK | INTR_MASK;
+const INTR_CHANGE: u16 = INTR_DPLX_CHANGE | INTR_SPD_CHANGE | INTR_LINK_CHANGE;
+
+/// Register 0x12: 10Base-T Configuration/Status Register
+const BTCSR: C22 = C22::vendor_specific::<0x12>();
+const BTCSR_INIT: u16 = 0x7800;
+
+/// Handles incoming hardware interrupts from the Davicom PHY.
+///
+/// This checks if the interrupt was caused by a link, speed, or duplex change.
+/// If so, it notifies the kernel to update the link state using `genphy_update_link`.
+///
+/// TODO: This function is currently unused because the Rust PHY abstraction `net::phy::Driver`
+/// does not yet expose a `handle_interrupt` callback. It is included here for the RFC.
+#[allow(dead_code)]
+fn dm9161_handle_interrupt(dev: &mut phy::Device) -> Result {
+    let irq_status = dev.read(INTR)?;
+
+    if (irq_status & INTR_CHANGE) == 0 {
+        return Ok(());
+    }
+
+    dev.genphy_update_link()?;
+
+    Ok(())
+}
+
+#[allow(dead_code)]
+fn dm9161_ack_interrupt(dev: &mut phy::Device) -> Result {
+    let _ = dev.read(INTR)?;
+    Ok(())
+}
+
+/// Configures whether the hardware alarm (interrupts) should be turned on or off.
+///
+/// It reads the current interrupt status requested by the OS by accessing the raw pointer.
+///
+/// TODO: This function is currently unused because the Rust PHY abstraction `net::phy::Driver`
+/// does not yet expose a `config_intr` callback. It is included here for the RFC.
+#[allow(dead_code)]
+fn dm9161_config_intr(dev: &mut phy::Device) -> Result {
+    let mut temp = dev.read(INTR)?;
+
+    let intr_enabled = unsafe {
+        let ptr = (dev as *mut phy::Device).cast::<bindings::phy_device>();
+        (*ptr).interrupts == bindings::PHY_INTERRUPT_ENABLED as u8
+    };
+
+    if intr_enabled {
+        dm9161_ack_interrupt(dev)?;
+        temp &= !INTR_STOP;
+        dev.write(INTR, temp)?;
+    } else {
+        temp |= INTR_STOP;
+        dev.write(INTR, temp)?;
+        dm9161_ack_interrupt(dev)?;
+    }
+    Ok(())
+}
+
+/// Configures PHY Auto-Negotiation.
+///
+/// Isolates the PHY during configuration, then calls the generic `genphy_config_aneg`
+/// via unsafe C bindings because Rust abstractions don't expose it directly yet.
+fn dm9161_config_aneg(dev: &mut phy::Device) -> Result {
+    dev.write(C22::BMCR, bindings::BMCR_ISOLATE as u16)?;
+
+    let err = unsafe {
+        let ptr = (dev as *mut phy::Device).cast::<bindings::phy_device>();
+        bindings::genphy_config_aneg(ptr)
+    };
+    to_result(err)?;
+
+    Ok(())
+}
+
+/// Initializes the Davicom PHY hardware upon detection.
+///
+/// Depending on the `interface` mode (MII vs RMII), the Scrambler Control Register (SCR)
+/// is configured. It relies on `C22::vendor_specific` addresses.
+///
+/// TODO: This function is currently unused because the Rust PHY abstraction `net::phy::Driver`
+/// does not yet expose a `config_init` callback. It is included here for the RFC.
+#[allow(dead_code)]
+fn dm9161_config_init(dev: &mut phy::Device) -> Result {
+    dev.write(C22::BMCR, bindings::BMCR_ISOLATE as u16)?;
+
+    let interface = unsafe {
+        let ptr = (dev as *mut phy::Device).cast::<bindings::phy_device>();
+        (*ptr).interface
+    };
+
+    let temp = match interface as core::ffi::c_uint {
+        bindings::phy_interface_t_PHY_INTERFACE_MODE_MII => SCR_INIT,
+        bindings::phy_interface_t_PHY_INTERFACE_MODE_RMII => SCR_INIT | SCR_RMII,
+        _ => return Err(code::EINVAL),
+    };
+
+    dev.write(SCR, temp)?;
+
+    dev.write(BTCSR, BTCSR_INIT)?;
+
+    dev.write(C22::BMCR, bindings::BMCR_ANENABLE as u16)?;
+
+    Ok(())
+}
+
+/// Representation of the Davicom DM9161E chip.
+struct DavicomDM9161E;
+
+#[vtable]
+impl Driver for DavicomDM9161E {
+    const NAME: &'static CStr = c"Davicom DM9161E";
+    const PHY_DEVICE_ID: phy::DeviceId = DeviceId::new_with_custom_mask(0x0181b880, 0x0ffffff0);
+    fn config_aneg(dev: &mut phy::Device) -> Result {
+        dm9161_config_aneg(dev)
+    }
+}
+
+struct DavicomDM9161A;
+
+#[vtable]
+impl Driver for DavicomDM9161A {
+    const NAME: &'static CStr = c"Davicom DM9161A";
+    const PHY_DEVICE_ID: phy::DeviceId = DeviceId::new_with_custom_mask(0x0181b8a0, 0x0ffffff0);
+    fn config_aneg(dev: &mut phy::Device) -> Result {
+        dm9161_config_aneg(dev)
+    }
+}
+
+kernel::module_phy_driver! {
+    drivers: [DavicomDM9161E, DavicomDM9161A],
+    device_table: [DeviceId::new_with_driver::<DavicomDM9161E>(), DeviceId::new_with_driver::<DavicomDM9161A>()],
+    name: "davicom_rust",
+    authors: ["Andy Fleming"],
+    description: "Davicom PHY Rust Driver",
+    license: "GPL"
+}
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-03-13  0:44 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-10 15:19 [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver Muchamad Coirul Anwar
2026-03-10 15:52 ` Andrew Lunn
2026-03-11  2:44   ` Muchamad Coirul Anwar
2026-03-11 13:36     ` Andrew Lunn
2026-03-13  0:44       ` Muchamad Coirul Anwar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox