[RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver

Muchamad Coirul Anwar posted 1 patch 4 weeks ago
drivers/net/phy/davicom_rust.rs | 165 ++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
create mode 100644 drivers/net/phy/davicom_rust.rs
[RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
Posted by Muchamad Coirul Anwar 4 weeks ago
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
Re: [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
Posted by Andrew Lunn 4 weeks ago
On Tue, Mar 10, 2026 at 10:19:35PM +0700, Muchamad Coirul Anwar wrote:
> 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.

How good is the QEMU emulation of this PHY? What features does it
support, compared to the real thing?

Adding support for interrupts to the Rust API seems useful, but does
the QEMU emulator support it? What emulated board are you using with
this PHY? Does that board have interrupts wired up? Can you triggering
interrupts by changing the link state?

> +/// Register 0x10: Scrambler Control Register (SCR)
> +const SCR: C22 = C22::vendor_specific::<0x10>();
> +const SCR_INIT: u16 = 0x0610;
> +const SCR_RMII: u16 = 0x0100;

Does QEMU have the scrambler? What happens if you wrongly configure
the scrambler.

> +/// 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)?;

What does the emulation do if you don't isolate the PHY? Is there an
errata for this PHY which tells you why it needs isolating?

I don't want to add code which cannot be tested. Ideally, it should be
tested on real hardware. We also are pushing back on duplicate C and
Rust. So if you want to write a Rust PHY driver, i suggest you find
some hardware which does not have a C driver, and add support for it
using Rust. Then i would be happy to expand the Rust binding as
needed.

    Andrew

---
pw-bot: cr
Re: [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
Posted by Muchamad Coirul Anwar 4 weeks ago
Hi Andrew,

Thank you for the response and for reviewing this RFC.

On Tue, Mar 10, 2026 at 10:52 PM Andrew Lunn <andrew@lunn.ch> wrote:
> How good is the QEMU emulation of this PHY? What features does it
> support, compared to the real thing?
>
> Adding support for interrupts to the Rust API seems useful, but does
> the QEMU emulator support it? What emulated board are you using with
> this PHY? Does that board have interrupts wired up? Can you triggering
> interrupts by changing the link state?

I sincerely apologize for the ambiguity in my commit message. There is
actually no specific QEMU emulation for the Davicom PHY. By "verified
via QEMU", I only meant that I booted the compiled `bzImage` in a
generic x86_64 QEMU VM to ensure the module initialization did not
cause any kernel panics or memory issues.

> Does QEMU have the scrambler? What happens if you wrongly configure
> the scrambler.
>
> What does the emulation do if you don't isolate the PHY? Is there an
> errata for this PHY which tells you why it needs isolating?

Because there is no actual hardware emulation, I unfortunately cannot
test these specific hardware behaviors. The logic in this RFC was
strictly a 1:1 blind translation of the existing `davicom.c` to see
where the Rust compiler and current PHY abstractions would stop me.

> I don't want to add code which cannot be tested. Ideally, it should be
> tested on real hardware. We also are pushing back on duplicate C and
> Rust. So if you want to write a Rust PHY driver, i suggest you find
> some hardware which does not have a C driver, and add support for it
> using Rust. Then i would be happy to expand the Rust binding as
> needed.

I completely agree with your policy on untested code and avoiding
C/Rust duplication. My primary goal with this RFC was exactly what you
offered: to highlight the missing bindings (like `config_intr`,
`config_init`, etc.) so the Rust API could be expanded.

I will gladly take your advice, drop this Davicom port, and look for a
new/unsupported PHY chip to write a proper Rust driver for. If you or
the netdev team have any specific upcoming or unsupported PHY chips in
mind that would be a good target for a first Rust driver, please let
me know. I would be more than happy to purchase the hardware and work
on it.

Thanks again for your time and guidance!

Best regards,

Muchamad Coirul Anwar
Re: [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
Posted by Andrew Lunn 3 weeks, 6 days ago
> I completely agree with your policy on untested code and avoiding
> C/Rust duplication. My primary goal with this RFC was exactly what you
> offered: to highlight the missing bindings (like `config_intr`,
> `config_init`, etc.) so the Rust API could be expanded.

Linux does not add an API without a user. If something is unused, it
is a pointless Maintenance burden.

> I will gladly take your advice, drop this Davicom port, and look for a
> new/unsupported PHY chip to write a proper Rust driver for. If you or
> the netdev team have any specific upcoming or unsupported PHY chips in
> mind that would be a good target for a first Rust driver, please let
> me know.

That is not really how Linux works. The Maintainers don't go out
searching for hardware which should be supported. Developers come to
us with patches.

One place you might look is Openwrt. They often have drivers for
hardware which never make it upstream to Mainline. Take such a driver,
buy the hardware, and port it to Rust. But please make sure it is
really a new device, not just a variant of an existing family of
devices which already has a driver.

	Andrew
Re: [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver
Posted by Muchamad Coirul Anwar 3 weeks, 5 days ago
Hi Andrew,

On Wed, Mar 11, 2026 at 20:36 Andrew Lunn <andrew@lunn.ch> wrote:
> That is not really how Linux works. The Maintainers don't go out
> searching for hardware which should be supported. Developers come to
> us with patches.
>
> One place you might look is Openwrt. They often have drivers for
> hardware which never make it upstream to Mainline. Take such a driver,
> buy the hardware, and port it to Rust. But please make sure it is
> really a new device, not just a variant of an existing family of
> devices which already has a driver.

Thank you for the excellent pointer regarding OpenWrt. I wasn't fully
aware of how many out-of-tree drivers are maintained there out of
necessity.

I will spend the next week researching the OpenWrt source tree for
unsupported PHY chips. My goal is to find a device that strictly meets
three criteria: it is a genuinely new chip (not a variant), the
hardware is physically purchasable, and the register datasheet is
publicly available without an NDA.

If hardware availability or closed documentation proves to be a hard
blocker after this timeboxed research, I will likely pivot to other
subsystems (such as IIO or hwmon) where hardware documentation is
generally more open, to continue my Rust driver contributions.

I will drop this Davicom RFC for now. Thank you again for your time,
review, and for pointing me in the right direction. It has been
incredibly helpful.

Best regards,

Muchamad Coirul Anwar