1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) Tehuti Networks Ltd. 3 // Copyright (C) 2024 FUJITA Tomonori <fujita.tomonori@gmail.com> 4 5 //! Applied Micro Circuits Corporation QT2025 PHY driver 6 //! 7 //! This driver is based on the vendor driver `QT2025_phy.c`. This source 8 //! and firmware can be downloaded on the EN-9320SFP+ support site. 9 //! 10 //! The QT2025 PHY integrates an Intel 8051 micro-controller. 11 12 use kernel::error::code; 13 use kernel::firmware::Firmware; 14 use kernel::io::poll::read_poll_timeout; 15 use kernel::net::phy::{ 16 self, 17 reg::{Mmd, C45}, 18 Driver, 19 }; 20 use kernel::prelude::*; 21 use kernel::sizes::{SZ_16K, SZ_8K}; 22 use kernel::time::Delta; 23 24 kernel::module_phy_driver! { 25 drivers: [PhyQT2025], 26 device_table: [ 27 phy::DeviceId::new_with_driver::<PhyQT2025>(), 28 ], 29 name: "qt2025_phy", 30 authors: ["FUJITA Tomonori <fujita.tomonori@gmail.com>"], 31 description: "AMCC QT2025 PHY driver", 32 license: "GPL", 33 firmware: ["qt2025-2.0.3.3.fw"], 34 } 35 36 struct PhyQT2025; 37 38 #[vtable] 39 impl Driver for PhyQT2025 { 40 const NAME: &'static CStr = c"QT2025 10Gpbs SFP+"; 41 const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x0043a400); 42 43 fn probe(dev: &mut phy::Device) -> Result<()> { 44 // Check the hardware revision code. 45 // Only 0xb3 works with this driver and firmware. 46 let hw_rev = dev.read(C45::new(Mmd::PMAPMD, 0xd001))?; 47 if (hw_rev >> 8) != 0xb3 { 48 return Err(code::ENODEV); 49 } 50 51 // `MICRO_RESETN`: hold the micro-controller in reset while configuring. 52 dev.write(C45::new(Mmd::PMAPMD, 0xc300), 0x0000)?; 53 // `SREFCLK_FREQ`: configure clock frequency of the micro-controller. 54 dev.write(C45::new(Mmd::PMAPMD, 0xc302), 0x0004)?; 55 // Non loopback mode. 56 dev.write(C45::new(Mmd::PMAPMD, 0xc319), 0x0038)?; 57 // `CUS_LAN_WAN_CONFIG`: select between LAN and WAN (WIS) mode. 58 dev.write(C45::new(Mmd::PMAPMD, 0xc31a), 0x0098)?; 59 // The following writes use standardized registers (3.38 through 60 // 3.41 5/10/25GBASE-R PCS test pattern seed B) for something else. 61 // We don't know what. 62 dev.write(C45::new(Mmd::PCS, 0x0026), 0x0e00)?; 63 dev.write(C45::new(Mmd::PCS, 0x0027), 0x0893)?; 64 dev.write(C45::new(Mmd::PCS, 0x0028), 0xa528)?; 65 dev.write(C45::new(Mmd::PCS, 0x0029), 0x0003)?; 66 // Configure transmit and recovered clock. 67 dev.write(C45::new(Mmd::PMAPMD, 0xa30a), 0x06e1)?; 68 // `MICRO_RESETN`: release the micro-controller from the reset state. 69 dev.write(C45::new(Mmd::PMAPMD, 0xc300), 0x0002)?; 70 // The micro-controller will start running from the boot ROM. 71 dev.write(C45::new(Mmd::PCS, 0xe854), 0x00c0)?; 72 73 let fw = Firmware::request(c"qt2025-2.0.3.3.fw", dev.as_ref())?; 74 if fw.data().len() > SZ_16K + SZ_8K { 75 return Err(code::EFBIG); 76 } 77 78 // The 24kB of program memory space is accessible by MDIO. 79 // The first 16kB of memory is located in the address range 3.8000h - 3.BFFFh. 80 // The next 8kB of memory is located at 4.8000h - 4.9FFFh. 81 let mut dst_offset = 0; 82 let mut dst_mmd = Mmd::PCS; 83 for (src_idx, val) in fw.data().iter().enumerate() { 84 if src_idx == SZ_16K { 85 // Start writing to the next register with no offset 86 dst_offset = 0; 87 dst_mmd = Mmd::PHYXS; 88 } 89 90 dev.write(C45::new(dst_mmd, 0x8000 + dst_offset), (*val).into())?; 91 92 dst_offset += 1; 93 } 94 // The micro-controller will start running from SRAM. 95 dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?; 96 97 read_poll_timeout( 98 || dev.read(C45::new(Mmd::PCS, 0xd7fd)), 99 |val| *val != 0x00 && *val != 0x10, 100 Delta::from_millis(50), 101 Delta::from_secs(3), 102 )?; 103 104 Ok(()) 105 } 106 107 fn read_status(dev: &mut phy::Device) -> Result<u16> { 108 dev.genphy_read_status::<C45>() 109 } 110 } 111