[PATCH v2 3/3] net: phy: add Rust Rockchip PHY driver

From: Christina Quast
Date: Thu Feb 01 2024 - 13:15:05 EST


This is the Rust implementation of drivers/net/phy/rockchip.c. The
features are equivalent. You can choose C or Rust version kernel
configuration.

Signed-off-by: Christina Quast <contact@xxxxxxxxxxxxxxxxxx>
---
drivers/net/phy/Kconfig | 8 +++
drivers/net/phy/Makefile | 4 ++
drivers/net/phy/rockchip_rust.rs | 131 +++++++++++++++++++++++++++++++++++++++
3 files changed, 143 insertions(+)

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 9e2672800f0b..8b73edb7e836 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -362,6 +362,14 @@ config ROCKCHIP_PHY
help
Currently supports the integrated Ethernet PHY.

+config ROCKCHIP_RUST_PHY
+ bool "Rust driver for Rockchip Ethernet PHYs"
+ depends on RUST_PHYLIB_ABSTRACTIONS && ROCKCHIP_PHY
+ help
+ Uses the Rust reference driver for Rockchip PHYs (rockchip_rust.ko).
+ The features are equivalent. It supports the integrated Ethernet PHY.
+
+
config SMSC_PHY
tristate "SMSC PHYs"
select CRC16
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 6097afd44392..045d2913bf2e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -94,7 +94,11 @@ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
+ifdef CONFIG_ROCKCHIP_RUST_PHY
+obj-$(CONFIG_ROCKCHIP_PHY) += rockchip_rust.o
+else
obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o
+endif
obj-$(CONFIG_SMSC_PHY) += smsc.o
obj-$(CONFIG_STE10XP) += ste10Xp.o
obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
diff --git a/drivers/net/phy/rockchip_rust.rs b/drivers/net/phy/rockchip_rust.rs
new file mode 100644
index 000000000000..17a1f94da8c1
--- /dev/null
+++ b/drivers/net/phy/rockchip_rust.rs
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2024 Christina Quast <contact@xxxxxxxxxxxxxxxxxx>
+
+//! Rust Rockchip PHY driver
+//!
+//! C version of this driver: [`drivers/net/phy/rockchip.c`](./rockchip.c)
+use kernel::{
+ c_str,
+ net::phy::{self, DeviceId, Driver},
+ prelude::*,
+ uapi,
+};
+
+kernel::module_phy_driver! {
+ drivers: [PhyRockchip],
+ device_table: [
+ DeviceId::new_with_driver::<PhyRockchip>(),
+ ],
+ name: "rust_asix_phy",
+ author: "FUJITA Tomonori <fujita.tomonori@xxxxxxxxx>",
+ description: "Rust Asix PHYs driver",
+ license: "GPL",
+}
+
+
+const MII_INTERNAL_CTRL_STATUS: u16 = 17;
+const SMI_ADDR_TSTCNTL: u16 = 20;
+const SMI_ADDR_TSTWRITE: u16 = 23;
+
+const MII_AUTO_MDIX_EN: u16 = bit(7);
+const MII_MDIX_EN: u16 = bit(6);
+
+const TSTCNTL_WR: u16 = bit(14) | bit(10);
+
+const TSTMODE_ENABLE: u16 = 0x400;
+const TSTMODE_DISABLE: u16 = 0x0;
+
+const WR_ADDR_A7CFG: u16 = 0x18;
+
+struct PhyRockchip;
+
+impl PhyRockchip {
+ /// Helper function for helper_integrated_phy_analog_init
+ fn helper_init_tstmode(dev: &mut phy::Device) -> Result {
+ // Enable access to Analog and DSP register banks
+ dev.write(SMI_ADDR_TSTCNTL, TSTMODE_ENABLE)?;
+ dev.write(SMI_ADDR_TSTCNTL, TSTMODE_DISABLE)?;
+ dev.write(SMI_ADDR_TSTCNTL, TSTMODE_ENABLE)
+ }
+
+ /// Helper function for helper_integrated_phy_analog_init
+ fn helper_close_tstmode(dev: &mut phy::Device) -> Result {
+ dev.write(SMI_ADDR_TSTCNTL, TSTMODE_DISABLE)
+ }
+
+ /// Helper function for rockchip_config_init
+ fn helper_integrated_phy_analog_init(dev: &mut phy::Device) -> Result {
+ Self::helper_init_tstmode(dev)?;
+ dev.write(SMI_ADDR_TSTWRITE, 0xB)?;
+ dev.write(SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG)?;
+ Self::helper_close_tstmode(dev)
+ }
+
+ /// Helper function for config_init
+ fn helper_config_init(dev: &mut phy::Device) -> Result {
+ let val = !MII_AUTO_MDIX_EN & dev.read(MII_INTERNAL_CTRL_STATUS)?;
+ dev.write(MII_INTERNAL_CTRL_STATUS, val)?;
+ Self::helper_integrated_phy_analog_init(dev)
+ }
+
+ fn helper_set_polarity(dev: &mut phy::Device, polarity: u8) -> Result {
+ let reg = !MII_AUTO_MDIX_EN & dev.read(MII_INTERNAL_CTRL_STATUS)?;
+ let val = match polarity as u32 {
+ // status: MDI; control: force MDI
+ uapi::ETH_TP_MDI => Some(reg & !MII_MDIX_EN),
+ // status: MDI-X; control: force MDI-X
+ uapi::ETH_TP_MDI_X => Some(reg | MII_MDIX_EN),
+ // uapi::ETH_TP_MDI_AUTO => control: auto-select
+ // uapi::ETH_TP_MDI_INVALID => status: unknown; control: unsupported
+ _ => None,
+ };
+ if let Some(v) = val {
+ if v != reg {
+ return dev.write(MII_INTERNAL_CTRL_STATUS, v);
+ }
+ }
+ Ok(())
+
+ }
+}
+
+#[vtable]
+impl Driver for PhyRockchip {
+ const FLAGS: u32 = 0;
+ const NAME: &'static CStr = c_str!("Rockchip integrated EPHY");
+ const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_custom_mask(0x1234d400, 0xfffffff0);
+
+ fn link_change_notify(dev: &mut phy::Device) {
+ // If mode switch happens from 10BT to 100BT, all DSP/AFE
+ // registers are set to default values. So any AFE/DSP
+ // registers have to be re-initialized in this case.
+ if dev.state() == phy::DeviceState::Running && dev.speed() == uapi::SPEED_100 {
+ if let Err(e) = Self::helper_integrated_phy_analog_init(dev) {
+ pr_err!("rockchip: integrated_phy_analog_init err: {:?}", e);
+ }
+ }
+ }
+
+ fn soft_reset(dev: &mut phy::Device) -> Result {
+ dev.genphy_soft_reset()
+ }
+
+ fn config_init(dev: &mut phy::Device) -> Result {
+ PhyRockchip::helper_config_init(dev)
+ }
+
+ fn config_aneg(dev: &mut phy::Device) -> Result {
+ PhyRockchip::helper_set_polarity(dev, dev.mdix())?;
+ dev.genphy_config_aneg()
+ }
+
+ fn suspend(dev: &mut phy::Device) -> Result {
+ dev.genphy_suspend()
+ }
+
+ fn resume(dev: &mut phy::Device) -> Result {
+ let _ = dev.genphy_resume();
+
+ PhyRockchip::helper_config_init(dev)
+ }
+}

--
2.43.0