xref: /linux/drivers/net/phy/qt2025.rs (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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