Add three new driver callbacks to the PHY abstraction: - config_init: Called to initialize the PHY after a reset. This is needed by nearly all real-world PHY drivers to configure vendor-specific registers (RGMII delays, LED settings, etc.). config_init was previously proposed in an RFC for the Rockchip PHY driver but not merged at that time. This re-introduces it in a form consistent with the existing vtable infrastructure. - read_page / write_page: Required by page-based PHY chips (Realtek, Marvell) for the kernel's paged register access infrastructure. When these callbacks are registered, phy_read_paged(), phy_write_paged(), and phy_modify_paged() use them to switch register pages. Both must be implemented together. Each callback follows the established Adapter pattern with unsafe extern "C" fn bridging to the Rust Driver trait method, and conditional registration in create_phy_driver() via HAS_* flags generated by the #[vtable] macro. Signed-off-by: Artem Lytkin --- rust/kernel/net/phy.rs | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 43d1ee360268b..8fb34d34fe7de 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -390,6 +390,51 @@ struct Adapter { } impl Adapter { + /// # Safety + /// + /// `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. + let dev = unsafe { Device::from_raw(phydev) }; + T::config_init(dev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `phydev` must be passed by the corresponding callback in `phy_driver`. + unsafe extern "C" fn read_page_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) }; + let page = T::read_page(dev)?; + Ok(page.into()) + }) + } + + /// # Safety + /// + /// `phydev` must be passed by the corresponding callback in `phy_driver`. + unsafe extern "C" fn write_page_callback( + phydev: *mut bindings::phy_device, + page: c_int, + ) -> 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) }; + let page = u16::try_from(page).map_err(|_| code::EINVAL)?; + T::write_page(dev, page)?; + Ok(0) + }) + } + /// # Safety /// /// `phydev` must be passed by the corresponding callback in `phy_driver`. @@ -582,6 +627,11 @@ pub const fn create_phy_driver() -> DriverVTable { flags: T::FLAGS, phy_id: T::PHY_DEVICE_ID.id(), phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(), + config_init: if T::HAS_CONFIG_INIT { + Some(Adapter::::config_init_callback) + } else { + None + }, soft_reset: if T::HAS_SOFT_RESET { Some(Adapter::::soft_reset_callback) } else { @@ -632,6 +682,16 @@ pub const fn create_phy_driver() -> DriverVTable { } else { None }, + read_page: if T::HAS_READ_PAGE { + Some(Adapter::::read_page_callback) + } else { + None + }, + write_page: if T::HAS_WRITE_PAGE { + Some(Adapter::::write_page_callback) + } else { + None + }, link_change_notify: if T::HAS_LINK_CHANGE_NOTIFY { Some(Adapter::::link_change_notify_callback) } else { @@ -659,6 +719,11 @@ pub trait Driver { /// The default id and mask are zero. const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_custom_mask(0, 0); + /// Called to initialize the PHY, including after a reset. + fn config_init(_dev: &mut Device) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + /// Issues a PHY software reset. fn soft_reset(_dev: &mut Device) -> Result { build_error!(VTABLE_DEFAULT_ERROR) @@ -711,6 +776,24 @@ fn write_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16, _val: u16) -> Result build_error!(VTABLE_DEFAULT_ERROR) } + /// Returns the current PHY register page number. + /// + /// Must be implemented together with [`Driver::write_page`]. The kernel's + /// paged register access infrastructure requires both callbacks to be + /// present. + fn read_page(_dev: &mut Device) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Sets the current PHY register page number. + /// + /// Must be implemented together with [`Driver::read_page`]. The kernel's + /// paged register access infrastructure requires both callbacks to be + /// present. + fn write_page(_dev: &mut Device, _page: u16) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + /// Callback for notification of link change. fn link_change_notify(_dev: &mut Device) {} } -- 2.43.0