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