From nobody Wed Apr 8 04:25:12 2026 Received: from mail-pj1-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7AA5D30BB89 for ; Tue, 10 Mar 2026 15:19:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773155987; cv=none; b=MEOK6hrH514rjH1JVSCxy/5rpPVsLiDBl7dazsTjM6Gp2nAqByzO3nEMi5CHxsFjU7Y2tx4KWhPPYmWq5rsLplTxh40alsoOxjhYqRPQOP3S1DqwC4+ae+pxUDafhOytU43wVMV+HZb2P/4VhthVmWEvg95pDQKFzttxJDND24k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773155987; c=relaxed/simple; bh=Yn4rjllrYHk/ctlmLPVwWjts5+Uwqds4FvaaiUMOC/M=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=s3hhDKHKr57kLpUEhzRuDyZ6CbWSUlcVsQVQoeqHiKAFx8WndEyFWFVL+XI5Ycvh9rgnm+qrk0XDhEy32jeuiKA6GXkua1rInvSBbsIIC3um1RBkGXKIHbd8e31OKWA8i31fTNIwQHPgb4Lg6+NbfjLbUMf2UZlZHedTWXIvJEg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MWh0omad; arc=none smtp.client-ip=209.85.216.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MWh0omad" Received: by mail-pj1-f49.google.com with SMTP id 98e67ed59e1d1-35995cb33a8so5227065a91.0 for ; Tue, 10 Mar 2026 08:19:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773155986; x=1773760786; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=/tbEPBXLOW6W4Gfj+URzMtQVIzs7pEYzk3RvSx2kF+M=; b=MWh0omadQaDkZEzZkUwMCqVo98TdS/1ONqS5uQa0Xo6j2NaD82SiY5MW61vCZJJPXx NikzYetZomo5HetmP5/qiPFKhSzzAku/yCqzLzkQyNKHMbk6l8h53mpkoa5J/OAESrmq oASvKkdf78Xc/7vGTBJrDQissoqtgkddvIbb5tCCh6HbR7v48lkhhIKeu52m+nDQSs5v ZJYM9PjVq4gOShgtJcbGkRNDeg/8pSEI6RNjoq7Cmbve8HzhiiRvg5mbL4vRfNr8M4rl hx3GyNujBrHftHc7muHV0IqY54afmxZ73nEZQO/hdzpRhpQyY6EfPZAXWkU1ksTPwzHB b0vw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773155986; x=1773760786; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=/tbEPBXLOW6W4Gfj+URzMtQVIzs7pEYzk3RvSx2kF+M=; b=o8DEArNkY475KqLYlFRVfU3yYF+ZbULw9+WzTCrALK9egmc60dLESIAAj+zwKmSkAW tBzExqnbyjofu1dZXzg3fGOXCxP/VNMyCjQtxmN3/H5jnGSJ/vy38Kwaiu81O6h9VlWC kaQm9j8GOEQMQFjLdyt1egc4FE95+ir2nBfQUFdg6+32tqxPqZ34FXH3a8duQ9ET/wUg FcYJFeuw/DvVzKE7O/N1AjoIcT98B36+IZLFjCHQs5nHKgxrLnbQI/Sx4GzmFjvWnw9+ Jm+h/xUNM1Zwwc9++eJXPFKMogtcWpfSj/20KmLMS5tWNat8umrhkK6fawCYT7hy6llb hoVA== X-Forwarded-Encrypted: i=1; AJvYcCWh2w2v0EELERu70Z4Wiy52h36KscG/ZogUHDJz8MUDWI3Aq7gExceaxolS7WmrUay2ei9zdBdzfv6iMow=@vger.kernel.org X-Gm-Message-State: AOJu0YyKa/fKGrkwXuYAiJnn58ybgKiK0a91uCQJFNhSAyZYqmOlE8b9 9ST8tGDxqrlIwOMjVF9+x+/+xgXhhejaeBrrjfwczM4Yz1fZHeHG9mhE X-Gm-Gg: ATEYQzzdiS4PZ/4nfw27Vmf4CnJ9yf3hiaGE0P3S33F1dH7N7md+whsEjjvwXS3Po6Q uiXuPngkCgAmtFgRYS+iCFfZnKU8k7iFxIduOTq2RcknaNpNW7LcZrms0wD8bVrnpd0E3XTSOMS DQX51oqY0meB4/Uz5XttAHf3b8xZ72YnD5tiQ+ZwHMYanG6RCbcNuVh2fk/+eLan5IabOMo+lFu BNSg7lEaLMBEWsVdq415bnRtEGPVR/fEw2u/OyUjL0/0pslVTAWm/+XyOCzD1NbVJHEbQQ4BpVY APVl7KS3uhXcXkeg8h2BeFVgpdTawqRG3umVgTSyiGsZQpJbRfsFZWtlHgsCpOFF1eF1jr4t/Ob qTdxiIwe02ix2+ytsYJbFKxHMaAfHt/eu3hk3IanVdI4U3zg2Om/wVY+ojgHg9f4F4RWEeRHi4e EDZME7zLUxjeUSnN/+jkV1P+Y5TH59aayzCyfhtw53xhu8RLgKkN0TqUNvBTVJT4wgYKsCkheIb m+R X-Received: by 2002:a17:90b:2c86:b0:359:8ca0:308d with SMTP id 98e67ed59e1d1-359f05cddb0mr3295256a91.14.1773155985702; Tue, 10 Mar 2026 08:19:45 -0700 (PDT) Received: from localhost.localdomain ([2001:448a:2003:2adb:898c:8fe1:aebd:4139]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-359f080db95sm5051182a91.9.2026.03.10.08.19.42 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Tue, 10 Mar 2026 08:19:45 -0700 (PDT) From: Muchamad Coirul Anwar To: andrew@lunn.ch, hkallweit1@gmail.com, ojeda@kernel.org Cc: linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Muchamad Coirul Anwar Subject: [RFC PATCH net-next] net: phy: rust: add experimental Davicom PHY driver Date: Tue, 10 Mar 2026 22:19:35 +0700 Message-ID: <20260310151935.33197-1-muchamadcoirulanwar@gmail.com> X-Mailer: git-send-email 2.50.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 =3D C22::vendor_specific::<0x10>(); +const SCR_INIT: u16 =3D 0x0610; +const SCR_RMII: u16 =3D 0x0100; + +/* DM9161 Interrupt Register */ + +/// Register 0x15: Interrupt Register +const INTR: C22 =3D C22::vendor_specific::<0x15>(); +const INTR_PEND: u16 =3D 0x8000; +const INTR_DPLX_MASK: u16 =3D 0x0800; +const INTR_SPD_MASK: u16 =3D 0x0400; +const INTR_LINK_MASK: u16 =3D 0x0200; +const INTR_MASK: u16 =3D 0x0100; +const INTR_DPLX_CHANGE: u16 =3D 0x0010; +const INTR_SPD_CHANGE: u16 =3D 0x0008; +const INTR_LINK_CHANGE: u16 =3D 0x0004; +const INTR_INIT: u16 =3D 0x0000; +const INTR_STOP: u16 =3D INTR_DPLX_MASK | INTR_SPD_MASK | INTR_LINK_MASK |= INTR_MASK; +const INTR_CHANGE: u16 =3D INTR_DPLX_CHANGE | INTR_SPD_CHANGE | INTR_LINK_= CHANGE; + +/// Register 0x12: 10Base-T Configuration/Status Register +const BTCSR: C22 =3D C22::vendor_specific::<0x12>(); +const BTCSR_INIT: u16 =3D 0x7800; + +/// Handles incoming hardware interrupts from the Davicom PHY. +/// +/// This checks if the interrupt was caused by a link, speed, or duplex ch= ange. +/// If so, it notifies the kernel to update the link state using `genphy_u= pdate_link`. +/// +/// TODO: This function is currently unused because the Rust PHY abstracti= on `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 =3D dev.read(INTR)?; + + if (irq_status & INTR_CHANGE) =3D=3D 0 { + return Ok(()); + } + + dev.genphy_update_link()?; + + Ok(()) +} + +#[allow(dead_code)] +fn dm9161_ack_interrupt(dev: &mut phy::Device) -> Result { + let _ =3D 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 abstracti= on `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 =3D dev.read(INTR)?; + + let intr_enabled =3D unsafe { + let ptr =3D (dev as *mut phy::Device).cast::= (); + (*ptr).interrupts =3D=3D bindings::PHY_INTERRUPT_ENABLED as u8 + }; + + if intr_enabled { + dm9161_ack_interrupt(dev)?; + temp &=3D !INTR_STOP; + dev.write(INTR, temp)?; + } else { + temp |=3D 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 direct= ly yet. +fn dm9161_config_aneg(dev: &mut phy::Device) -> Result { + dev.write(C22::BMCR, bindings::BMCR_ISOLATE as u16)?; + + let err =3D unsafe { + let ptr =3D (dev as *mut phy::Device).cast::= (); + 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 abstracti= on `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 =3D unsafe { + let ptr =3D (dev as *mut phy::Device).cast::= (); + (*ptr).interface + }; + + let temp =3D match interface as core::ffi::c_uint { + bindings::phy_interface_t_PHY_INTERFACE_MODE_MII =3D> SCR_INIT, + bindings::phy_interface_t_PHY_INTERFACE_MODE_RMII =3D> SCR_INIT | = SCR_RMII, + _ =3D> 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 =3D c"Davicom DM9161E"; + const PHY_DEVICE_ID: phy::DeviceId =3D 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 =3D c"Davicom DM9161A"; + const PHY_DEVICE_ID: phy::DeviceId =3D 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::(), DeviceId= ::new_with_driver::()], + name: "davicom_rust", + authors: ["Andy Fleming"], + description: "Davicom PHY Rust Driver", + license: "GPL" +} --=20 2.50.0