1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com> 3 4 //! Rust Asix PHYs driver 5 //! 6 //! C version of this driver: [`drivers/net/phy/ax88796b.c`](./ax88796b.c) 7 use kernel::{ 8 c_str, 9 net::phy::{self, reg::C22, DeviceId, Driver}, 10 prelude::*, 11 uapi, 12 }; 13 14 kernel::module_phy_driver! { 15 drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B], 16 device_table: [ 17 DeviceId::new_with_driver::<PhyAX88772A>(), 18 DeviceId::new_with_driver::<PhyAX88772C>(), 19 DeviceId::new_with_driver::<PhyAX88796B>() 20 ], 21 name: "rust_asix_phy", 22 author: "FUJITA Tomonori <fujita.tomonori@gmail.com>", 23 description: "Rust Asix PHYs driver", 24 license: "GPL", 25 } 26 27 const BMCR_SPEED100: u16 = uapi::BMCR_SPEED100 as u16; 28 const BMCR_FULLDPLX: u16 = uapi::BMCR_FULLDPLX as u16; 29 30 // Performs a software PHY reset using the standard 31 // BMCR_RESET bit and poll for the reset bit to be cleared. 32 // Toggle BMCR_RESET bit off to accommodate broken AX8796B PHY implementation 33 // such as used on the Individual Computers' X-Surf 100 Zorro card. 34 fn asix_soft_reset(dev: &mut phy::Device) -> Result { 35 dev.write(C22::BMCR, 0)?; 36 dev.genphy_soft_reset() 37 } 38 39 struct PhyAX88772A; 40 41 #[vtable] 42 impl Driver for PhyAX88772A { 43 const FLAGS: u32 = phy::flags::IS_INTERNAL; 44 const NAME: &'static CStr = c_str!("Asix Electronics AX88772A"); 45 const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1861); 46 47 // AX88772A is not working properly with some old switches (NETGEAR EN 108TP): 48 // after autoneg is done and the link status is reported as active, the MII_LPA 49 // register is 0. This issue is not reproducible on AX88772C. 50 fn read_status(dev: &mut phy::Device) -> Result<u16> { 51 dev.genphy_update_link()?; 52 if !dev.is_link_up() { 53 return Ok(0); 54 } 55 // If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve 56 // linkmode so use MII_BMCR as default values. 57 let ret = dev.read(C22::BMCR)?; 58 59 if ret & BMCR_SPEED100 != 0 { 60 dev.set_speed(uapi::SPEED_100); 61 } else { 62 dev.set_speed(uapi::SPEED_10); 63 } 64 65 let duplex = if ret & BMCR_FULLDPLX != 0 { 66 phy::DuplexMode::Full 67 } else { 68 phy::DuplexMode::Half 69 }; 70 dev.set_duplex(duplex); 71 72 dev.genphy_read_lpa()?; 73 74 if dev.is_autoneg_enabled() && dev.is_autoneg_completed() { 75 dev.resolve_aneg_linkmode(); 76 } 77 78 Ok(0) 79 } 80 81 fn suspend(dev: &mut phy::Device) -> Result { 82 dev.genphy_suspend() 83 } 84 85 fn resume(dev: &mut phy::Device) -> Result { 86 dev.genphy_resume() 87 } 88 89 fn soft_reset(dev: &mut phy::Device) -> Result { 90 asix_soft_reset(dev) 91 } 92 93 fn link_change_notify(dev: &mut phy::Device) { 94 // Reset PHY, otherwise MII_LPA will provide outdated information. 95 // This issue is reproducible only with some link partner PHYs. 96 if dev.state() == phy::DeviceState::NoLink { 97 let _ = dev.init_hw(); 98 let _ = dev.start_aneg(); 99 } 100 } 101 } 102 103 struct PhyAX88772C; 104 105 #[vtable] 106 impl Driver for PhyAX88772C { 107 const FLAGS: u32 = phy::flags::IS_INTERNAL; 108 const NAME: &'static CStr = c_str!("Asix Electronics AX88772C"); 109 const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1881); 110 111 fn suspend(dev: &mut phy::Device) -> Result { 112 dev.genphy_suspend() 113 } 114 115 fn resume(dev: &mut phy::Device) -> Result { 116 dev.genphy_resume() 117 } 118 119 fn soft_reset(dev: &mut phy::Device) -> Result { 120 asix_soft_reset(dev) 121 } 122 } 123 124 struct PhyAX88796B; 125 126 #[vtable] 127 impl Driver for PhyAX88796B { 128 const NAME: &'static CStr = c_str!("Asix Electronics AX88796B"); 129 const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_model_mask(0x003b1841); 130 131 fn soft_reset(dev: &mut phy::Device) -> Result { 132 asix_soft_reset(dev) 133 } 134 } 135