├── README.md ├── rtl_eeprom.h ├── r8125_fiber.h ├── r8125_firmware.h ├── rtltool.h ├── r8125_rss.h ├── r8125_realwow.h ├── r8125_ptp.h ├── Makefile ├── r8125_dash.h ├── rtl_eeprom.c ├── r8125_firmware.c ├── rtltool.c ├── r8125_fiber.c ├── r8125_dash.c ├── r8125_rss.c └── r8125_ptp.c /README.md: -------------------------------------------------------------------------------- 1 | # Realtek RTL8125 2.5GbE PCIe 2 | 3 | This repository contains the source code provided by Realtek for the RTL8125 2.5GbE PCIe controllers. 4 | 5 | The source code is provided by Realtek as-is, without any kind of changelog. Git is only used for tracking down the changes introduced between versions. 6 | 7 | ## Original sources 8 | 9 | You can find the original files provided by Realtek [here](https://www.realtek.com/Download/List?cate_id=584). 10 | 11 | The same files from Realtek are provided as [release assets](https://github.com/openwrt/rtl8125/releases). 12 | 13 | ## Disclaimer 14 | 15 | Bug reports or issues should be reported directly to [Realtek](https://www.realtek.com). 16 | 17 | This repository is used for OpenWrt development because the files provided by Realtek are protected with CAPTCHAs and can't be used for creating OpenWrt packages. 18 | -------------------------------------------------------------------------------- /rtl_eeprom.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | //EEPROM opcodes 36 | #define RTL_EEPROM_READ_OPCODE 06 37 | #define RTL_EEPROM_WRITE_OPCODE 05 38 | #define RTL_EEPROM_ERASE_OPCODE 07 39 | #define RTL_EEPROM_EWEN_OPCODE 19 40 | #define RTL_EEPROM_EWDS_OPCODE 16 41 | 42 | #define RTL_CLOCK_RATE 3 43 | 44 | void rtl8125_eeprom_type(struct rtl8125_private *tp); 45 | void rtl8125_eeprom_cleanup(struct rtl8125_private *tp); 46 | u16 rtl8125_eeprom_read_sc(struct rtl8125_private *tp, u16 reg); 47 | void rtl8125_eeprom_write_sc(struct rtl8125_private *tp, u16 reg, u16 data); 48 | void rtl8125_shift_out_bits(struct rtl8125_private *tp, int data, int count); 49 | u16 rtl8125_shift_in_bits(struct rtl8125_private *tp); 50 | void rtl8125_raise_clock(struct rtl8125_private *tp, u8 *x); 51 | void rtl8125_lower_clock(struct rtl8125_private *tp, u8 *x); 52 | void rtl8125_stand_by(struct rtl8125_private *tp); 53 | void rtl8125_set_eeprom_sel_low(struct rtl8125_private *tp); 54 | -------------------------------------------------------------------------------- /r8125_fiber.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_R8125_FIBER_H 36 | #define _LINUX_R8125_FIBER_H 37 | 38 | enum { 39 | FIBER_MODE_NIC_ONLY = 0, 40 | FIBER_MODE_RTL8125D_RTL8221D, 41 | FIBER_MODE_MAX 42 | }; 43 | 44 | enum { 45 | FIBER_STAT_NOT_CHECKED = 0, 46 | FIBER_STAT_DISCONNECT, 47 | FIBER_STAT_CONNECT_GPO_C45, 48 | FIBER_STAT_MAX 49 | }; 50 | 51 | #define HW_FIBER_MODE_ENABLED(_M) ((_M)->HwFiberModeVer > 0) 52 | #define HW_FIBER_STATUS_CONNECTED(_M) (((_M)->HwFiberStat == FIBER_STAT_CONNECT_GPO_C45)) 53 | #define HW_FIBER_STATUS_DISCONNECTED(_M) ((_M)->HwFiberStat == FIBER_STAT_DISCONNECT) 54 | 55 | struct rtl8125_private; 56 | 57 | void rtl8125_hw_fiber_phy_config(struct rtl8125_private *tp); 58 | void rtl8125_check_fiber_mode_support(struct rtl8125_private *tp); 59 | void rtl8125_fiber_mdio_write( struct rtl8125_private *tp, u32 reg, u16 val); 60 | u16 rtl8125_fiber_mdio_read(struct rtl8125_private *tp, u32 reg); 61 | unsigned int rtl8125_fiber_link_ok(struct net_device *dev); 62 | 63 | #endif /* _LINUX_R8125_FIBER_H */ 64 | -------------------------------------------------------------------------------- /r8125_firmware.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_rtl8125_FIRMWARE_H 36 | #define _LINUX_rtl8125_FIRMWARE_H 37 | 38 | #include 39 | #include 40 | 41 | struct rtl8125_private; 42 | typedef void (*rtl8125_fw_write_t)(struct rtl8125_private *tp, u16 reg, u16 val); 43 | typedef u32 (*rtl8125_fw_read_t)(struct rtl8125_private *tp, u16 reg); 44 | 45 | #define RTL8125_VER_SIZE 32 46 | 47 | struct rtl8125_fw { 48 | rtl8125_fw_write_t phy_write; 49 | rtl8125_fw_read_t phy_read; 50 | rtl8125_fw_write_t mac_mcu_write; 51 | rtl8125_fw_read_t mac_mcu_read; 52 | const struct firmware *fw; 53 | const char *fw_name; 54 | struct device *dev; 55 | 56 | char version[RTL8125_VER_SIZE]; 57 | 58 | struct rtl8125_fw_phy_action { 59 | __le32 *code; 60 | size_t size; 61 | } phy_action; 62 | }; 63 | 64 | int rtl8125_fw_request_firmware(struct rtl8125_fw *rtl_fw); 65 | void rtl8125_fw_release_firmware(struct rtl8125_fw *rtl_fw); 66 | void rtl8125_fw_write_firmware(struct rtl8125_private *tp, struct rtl8125_fw *rtl_fw); 67 | 68 | #endif /* _LINUX_rtl8125_FIRMWARE_H */ 69 | -------------------------------------------------------------------------------- /rtltool.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_RTLTOOL_H 36 | #define _LINUX_RTLTOOL_H 37 | 38 | #define SIOCRTLTOOL SIOCDEVPRIVATE+1 39 | 40 | enum rtl_cmd { 41 | RTLTOOL_READ_MAC=0, 42 | RTLTOOL_WRITE_MAC, 43 | RTLTOOL_READ_PHY, 44 | RTLTOOL_WRITE_PHY, 45 | RTLTOOL_READ_EPHY, 46 | RTLTOOL_WRITE_EPHY, 47 | RTLTOOL_READ_ERI, 48 | RTLTOOL_WRITE_ERI, 49 | RTLTOOL_READ_PCI, 50 | RTLTOOL_WRITE_PCI, 51 | RTLTOOL_READ_EEPROM, 52 | RTLTOOL_WRITE_EEPROM, 53 | 54 | RTL_READ_OOB_MAC, 55 | RTL_WRITE_OOB_MAC, 56 | 57 | RTL_ENABLE_PCI_DIAG, 58 | RTL_DISABLE_PCI_DIAG, 59 | 60 | RTL_READ_MAC_OCP, 61 | RTL_WRITE_MAC_OCP, 62 | 63 | RTL_DIRECT_READ_PHY_OCP, 64 | RTL_DIRECT_WRITE_PHY_OCP, 65 | 66 | RTL_READ_FIBER_PHY, 67 | RTL_WRITE_FIBER_PHY, 68 | 69 | RTLTOOL_INVALID 70 | }; 71 | 72 | struct rtltool_cmd { 73 | __u32 cmd; 74 | __u32 offset; 75 | __u32 len; 76 | __u32 data; 77 | }; 78 | 79 | enum mode_access { 80 | MODE_NONE=0, 81 | MODE_READ, 82 | MODE_WRITE 83 | }; 84 | 85 | #ifdef __KERNEL__ 86 | int rtl8125_tool_ioctl(struct rtl8125_private *tp, struct ifreq *ifr); 87 | #endif 88 | 89 | #endif /* _LINUX_RTLTOOL_H */ 90 | -------------------------------------------------------------------------------- /r8125_rss.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_rtl8125_RSS_H 36 | #define _LINUX_rtl8125_RSS_H 37 | 38 | #include 39 | #include 40 | 41 | #define RTL8125_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */ 42 | #define RTL8125_MAX_INDIRECTION_TABLE_ENTRIES 128 43 | 44 | enum rtl8125_rss_flag { 45 | RTL_8125_RSS_FLAG_HASH_UDP_IPV4 = (1 << 0), 46 | RTL_8125_RSS_FLAG_HASH_UDP_IPV6 = (1 << 1), 47 | }; 48 | 49 | struct rtl8125_private; 50 | struct RxDesc; 51 | 52 | int rtl8125_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 53 | u32 *rule_locs); 54 | int rtl8125_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd); 55 | u32 rtl8125_get_rxfh_key_size(struct net_device *netdev); 56 | u32 rtl8125_rss_indir_size(struct net_device *netdev); 57 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,8,0) 58 | int rtl8125_get_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh); 59 | int rtl8125_set_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh, 60 | struct netlink_ext_ack *extack); 61 | #else 62 | int rtl8125_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, 63 | u8 *hfunc); 64 | int rtl8125_set_rxfh(struct net_device *netdev, const u32 *indir, 65 | const u8 *key, const u8 hfunc); 66 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,8,0) */ 67 | void rtl8125_rx_hash(struct rtl8125_private *tp, 68 | struct RxDesc *desc, 69 | struct sk_buff *skb); 70 | void _rtl8125_config_rss(struct rtl8125_private *tp); 71 | void rtl8125_config_rss(struct rtl8125_private *tp); 72 | void rtl8125_init_rss(struct rtl8125_private *tp); 73 | u32 rtl8125_rss_indir_tbl_entries(struct rtl8125_private *tp); 74 | void rtl8125_disable_rss(struct rtl8125_private *tp); 75 | 76 | #endif /* _LINUX_rtl8125_RSS_H */ 77 | -------------------------------------------------------------------------------- /r8125_realwow.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_R8125_REALWOW_H 36 | #define _LINUX_R8125_REALWOW_H 37 | 38 | #define SIOCDEVPRIVATE_RTLREALWOW SIOCDEVPRIVATE+3 39 | 40 | #define MAX_RealWoW_KCP_SIZE (100) 41 | #define MAX_RealWoW_Payload (64) 42 | 43 | #define KA_TX_PACKET_SIZE (100) 44 | #define KA_WAKEUP_PATTERN_SIZE (120) 45 | 46 | //HwSuppKeepAliveOffloadVer 47 | #define HW_SUPPORT_KCP_OFFLOAD(_M) ((_M)->HwSuppKCPOffloadVer > 0) 48 | 49 | enum rtl_realwow_cmd { 50 | 51 | RTL_REALWOW_SET_KCP_DISABLE=0, 52 | RTL_REALWOW_SET_KCP_INFO, 53 | RTL_REALWOW_SET_KCP_CONTENT, 54 | 55 | RTL_REALWOW_SET_KCP_ACKPKTINFO, 56 | RTL_REALWOW_SET_KCP_WPINFO, 57 | RTL_REALWOW_SET_KCPDHCP_TIMEOUT, 58 | 59 | RTLT_REALWOW_COMMAND_INVALID 60 | }; 61 | 62 | struct rtl_realwow_ioctl_struct { 63 | __u32 cmd; 64 | __u32 offset; 65 | __u32 len; 66 | union { 67 | __u32 data; 68 | void *data_buffer; 69 | }; 70 | }; 71 | 72 | typedef struct _MP_KCPInfo { 73 | u8 DIPv4[4]; 74 | u8 MacID[6]; 75 | u16 UdpPort[2]; 76 | u8 PKTLEN[2]; 77 | 78 | u16 ackLostCnt; 79 | u8 KCP_WakePattern[MAX_RealWoW_Payload]; 80 | u8 KCP_AckPacket[MAX_RealWoW_Payload]; 81 | u32 KCP_interval; 82 | u8 KCP_WakePattern_Len; 83 | u8 KCP_AckPacket_Len; 84 | u8 KCP_TxPacket[2][KA_TX_PACKET_SIZE]; 85 | } MP_KCP_INFO, *PMP_KCP_INFO; 86 | 87 | typedef struct _KCPInfo { 88 | u32 nId; // = id 89 | u8 DIPv4[4]; 90 | u8 MacID[6]; 91 | u16 UdpPort; 92 | u16 PKTLEN; 93 | } KCPInfo, *PKCPInfo; 94 | 95 | typedef struct _KCPContent { 96 | u32 id; // = id 97 | u32 mSec; // = msec 98 | u32 size; // =size 99 | u8 bPacket[MAX_RealWoW_KCP_SIZE]; // put packet here 100 | } KCPContent, *PKCPContent; 101 | 102 | typedef struct _RealWoWAckPktInfo { 103 | u16 ackLostCnt; 104 | u16 patterntSize; 105 | u8 pattern[MAX_RealWoW_Payload]; 106 | } RealWoWAckPktInfo,*PRealWoWAckPktInfo; 107 | 108 | typedef struct _RealWoWWPInfo { 109 | u16 patterntSize; 110 | u8 pattern[MAX_RealWoW_Payload]; 111 | } RealWoWWPInfo,*PRealWoWWPInfo; 112 | 113 | int rtl8125_realwow_ioctl(struct net_device *dev, struct ifreq *ifr); 114 | void rtl8125_realwow_hw_init(struct net_device *dev); 115 | void rtl8125_get_realwow_hw_version(struct net_device *dev); 116 | void rtl8125_set_realwow_d3_para(struct net_device *dev); 117 | 118 | #endif /* _LINUX_R8125_REALWOW_H */ 119 | -------------------------------------------------------------------------------- /r8125_ptp.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_rtl8125_PTP_H 36 | #define _LINUX_rtl8125_PTP_H 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0) 45 | #define PTP_MSGTYPE_SYNC 0x0 46 | #define PTP_MSGTYPE_DELAY_REQ 0x1 47 | #define PTP_MSGTYPE_PDELAY_REQ 0x2 48 | #define PTP_MSGTYPE_PDELAY_RESP 0x3 49 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0) */ 50 | 51 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) 52 | struct clock_identity { 53 | u8 id[8]; 54 | } __packed; 55 | 56 | struct port_identity { 57 | struct clock_identity clock_identity; 58 | __be16 port_number; 59 | } __packed; 60 | 61 | struct ptp_header { 62 | u8 tsmt; /* transportSpecific | messageType */ 63 | u8 ver; /* reserved | versionPTP */ 64 | __be16 message_length; 65 | u8 domain_number; 66 | u8 reserved1; 67 | u8 flag_field[2]; 68 | __be64 correction; 69 | __be32 reserved2; 70 | struct port_identity source_port_identity; 71 | __be16 sequence_id; 72 | u8 control; 73 | u8 log_message_interval; 74 | } __packed; 75 | 76 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) */ 77 | 78 | struct rtl8125_ptp_info { 79 | s64 time_sec; 80 | u32 time_ns; 81 | u16 ts_info; 82 | }; 83 | 84 | #ifndef _STRUCT_TIMESPEC 85 | #define _STRUCT_TIMESPEC 86 | struct timespec { 87 | __kernel_old_time_t tv_sec; /* seconds */ 88 | long tv_nsec; /* nanoseconds */ 89 | }; 90 | #endif 91 | 92 | enum PTP_CMD_TYPE { 93 | PTP_CMD_SET_LOCAL_TIME = 0, 94 | PTP_CMD_DRIFT_LOCAL_TIME, 95 | PTP_CMD_LATCHED_LOCAL_TIME, 96 | }; 97 | 98 | #define PTP_CLKADJ_MODE_SET BIT_0 99 | 100 | enum PTP_CLKADJ_MOD_TYPE { 101 | NO_FUNCTION = 0, 102 | CLKADJ_MODE_SET = 1, 103 | RESERVED = 2, 104 | DIRECT_READ = 4, 105 | DIRECT_WRITE = 6, 106 | INCREMENT_STEP = 8, 107 | DECREMENT_STEP = 10, 108 | RATE_READ = 12, 109 | RATE_WRITE = 14, 110 | }; 111 | 112 | enum PTP_INSR_TYPE { 113 | EVENT_CAP_INTR = (1 << 0), 114 | TRIG_GEN_INTR = (1 << 1), 115 | RX_TS_INTR = (1 << 2), 116 | TX_TX_INTR = (1 << 3), 117 | }; 118 | 119 | enum PTP_TRX_TS_STA_REG { 120 | TRX_TS_RD = (1 << 0), 121 | TRXTS_SEL = (1 << 1), 122 | RX_TS_PDLYRSP_RDY = (1 << 8), 123 | RX_TS_PDLYREQ_RDY = (1 << 9), 124 | RX_TS_DLYREQ_RDY = (1 << 10), 125 | RX_TS_SYNC_RDY = (1 << 11), 126 | TX_TS_PDLYRSP_RDY = (1 << 12), 127 | TX_TS_PDLYREQ_RDY = (1 << 13), 128 | TX_TS_DLYREQ_RDY = (1 << 14), 129 | TX_TS_SYNC_RDY = (1 << 15), 130 | }; 131 | 132 | #define RTL_FLAG_RX_HWTSTAMP_ENABLED BIT_0 133 | 134 | struct rtl8125_private; 135 | struct RxDescV3; 136 | 137 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0) 138 | int rtl8125_get_ts_info(struct net_device *netdev, 139 | struct ethtool_ts_info *info); 140 | #else 141 | int rtl8125_get_ts_info(struct net_device *netdev, 142 | struct kernel_ethtool_ts_info *info); 143 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0) */ 144 | 145 | void rtl8125_ptp_reset(struct rtl8125_private *tp); 146 | void rtl8125_ptp_init(struct rtl8125_private *tp); 147 | void rtl8125_ptp_suspend(struct rtl8125_private *tp); 148 | void rtl8125_ptp_stop(struct rtl8125_private *tp); 149 | 150 | int rtl8125_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); 151 | 152 | void rtl8125_rx_mac_ptp_pktstamp(struct rtl8125_private *tp, struct sk_buff *skb, 153 | struct RxDescV3 *descv3); 154 | 155 | void rtl8125_set_phy_local_time(struct rtl8125_private *tp); 156 | 157 | void rtl8125_rx_phy_ptp_timestamp(struct rtl8125_private *tp, struct sk_buff *skb); 158 | 159 | #endif /* _LINUX_rtl8125_PTP_H */ 160 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | ################################################################################ 3 | # 4 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 5 | # controllers with PCI-Express interface. 6 | # 7 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 8 | # 9 | # This program is free software; you can redistribute it and/or modify it 10 | # under the terms of the GNU General Public License as published by the Free 11 | # Software Foundation; either version 2 of the License, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | # more details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program; if not, see . 21 | # 22 | # Author: 23 | # Realtek NIC software team 24 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 25 | # 26 | ################################################################################ 27 | 28 | ################################################################################ 29 | # This product is covered by one or more of the following patents: 30 | # US6,570,884, US6,115,776, and US6,327,625. 31 | ################################################################################ 32 | 33 | CONFIG_SOC_LAN = y 34 | ENABLE_FIBER_SUPPORT = n 35 | ENABLE_REALWOW_SUPPORT = n 36 | ENABLE_DASH_SUPPORT = n 37 | CONFIG_DOWN_SPEED_100 = n 38 | CONFIG_ASPM = y 39 | ENABLE_S5WOL = y 40 | ENABLE_S5_KEEP_CURR_MAC = n 41 | ENABLE_EEE = y 42 | ENABLE_S0_MAGIC_PACKET = n 43 | ENABLE_TX_NO_CLOSE = y 44 | ENABLE_MULTIPLE_TX_QUEUE = n 45 | ENABLE_PTP_SUPPORT = n 46 | ENABLE_PTP_MASTER_MODE = n 47 | ENABLE_RSS_SUPPORT = n 48 | ENABLE_LIB_SUPPORT = n 49 | ENABLE_USE_FIRMWARE_FILE = n 50 | DISABLE_WOL_SUPPORT = n 51 | DISABLE_MULTI_MSIX_VECTOR = n 52 | ENABLE_DOUBLE_VLAN = n 53 | ENABLE_PAGE_REUSE = n 54 | ENABLE_RX_PACKET_FRAGMENT = n 55 | ENABLE_GIGA_LITE = y 56 | 57 | ifneq ($(KERNELRELEASE),) 58 | obj-m := r8125.o 59 | r8125-objs := r8125_n.o rtl_eeprom.o rtltool.o 60 | ifeq ($(CONFIG_SOC_LAN), y) 61 | EXTRA_CFLAGS += -DCONFIG_SOC_LAN 62 | endif 63 | ifeq ($(ENABLE_FIBER_SUPPORT), y) 64 | r8125-objs += r8125_fiber.o 65 | EXTRA_CFLAGS += -DENABLE_FIBER_SUPPORT 66 | endif 67 | ifeq ($(ENABLE_REALWOW_SUPPORT), y) 68 | r8125-objs += r8125_realwow.o 69 | EXTRA_CFLAGS += -DENABLE_REALWOW_SUPPORT 70 | endif 71 | ifeq ($(ENABLE_DASH_SUPPORT), y) 72 | r8125-objs += r8125_dash.o 73 | EXTRA_CFLAGS += -DENABLE_DASH_SUPPORT 74 | endif 75 | EXTRA_CFLAGS += -DCONFIG_R8125_NAPI 76 | EXTRA_CFLAGS += -DCONFIG_R8125_VLAN 77 | ifeq ($(CONFIG_DOWN_SPEED_100), y) 78 | EXTRA_CFLAGS += -DCONFIG_DOWN_SPEED_100 79 | endif 80 | ifeq ($(CONFIG_ASPM), y) 81 | EXTRA_CFLAGS += -DCONFIG_ASPM 82 | endif 83 | ifeq ($(ENABLE_S5WOL), y) 84 | EXTRA_CFLAGS += -DENABLE_S5WOL 85 | endif 86 | ifeq ($(ENABLE_S5_KEEP_CURR_MAC), y) 87 | EXTRA_CFLAGS += -DENABLE_S5_KEEP_CURR_MAC 88 | endif 89 | ifeq ($(ENABLE_EEE), y) 90 | EXTRA_CFLAGS += -DENABLE_EEE 91 | endif 92 | ifeq ($(ENABLE_S0_MAGIC_PACKET), y) 93 | EXTRA_CFLAGS += -DENABLE_S0_MAGIC_PACKET 94 | endif 95 | ifeq ($(ENABLE_TX_NO_CLOSE), y) 96 | EXTRA_CFLAGS += -DENABLE_TX_NO_CLOSE 97 | endif 98 | ifeq ($(ENABLE_MULTIPLE_TX_QUEUE), y) 99 | EXTRA_CFLAGS += -DENABLE_MULTIPLE_TX_QUEUE 100 | endif 101 | ifeq ($(ENABLE_PTP_SUPPORT), y) 102 | r8125-objs += r8125_ptp.o 103 | EXTRA_CFLAGS += -DENABLE_PTP_SUPPORT 104 | endif 105 | ifeq ($(ENABLE_PTP_MASTER_MODE), y) 106 | EXTRA_CFLAGS += -DENABLE_PTP_MASTER_MODE 107 | endif 108 | ifeq ($(ENABLE_RSS_SUPPORT), y) 109 | r8125-objs += r8125_rss.o 110 | EXTRA_CFLAGS += -DENABLE_RSS_SUPPORT 111 | endif 112 | ifeq ($(ENABLE_LIB_SUPPORT), y) 113 | r8125-objs += r8125_lib.o 114 | EXTRA_CFLAGS += -DENABLE_LIB_SUPPORT 115 | endif 116 | ifeq ($(ENABLE_USE_FIRMWARE_FILE), y) 117 | r8125-objs += r8125_firmware.o 118 | EXTRA_CFLAGS += -DENABLE_USE_FIRMWARE_FILE 119 | endif 120 | ifeq ($(DISABLE_WOL_SUPPORT), y) 121 | EXTRA_CFLAGS += -DDISABLE_WOL_SUPPORT 122 | endif 123 | ifeq ($(DISABLE_MULTI_MSIX_VECTOR), y) 124 | EXTRA_CFLAGS += -DDISABLE_MULTI_MSIX_VECTOR 125 | endif 126 | ifeq ($(ENABLE_DOUBLE_VLAN), y) 127 | EXTRA_CFLAGS += -DENABLE_DOUBLE_VLAN 128 | endif 129 | ifeq ($(ENABLE_PAGE_REUSE), y) 130 | EXTRA_CFLAGS += -DENABLE_PAGE_REUSE 131 | endif 132 | ifeq ($(ENABLE_RX_PACKET_FRAGMENT), y) 133 | EXTRA_CFLAGS += -DENABLE_RX_PACKET_FRAGMENT 134 | endif 135 | ifeq ($(ENABLE_GIGA_LITE), y) 136 | EXTRA_CFLAGS += -DENABLE_GIGA_LITE 137 | endif 138 | 139 | # Backward compatibility 140 | ccflags-y += $(EXTRA_CFLAGS) 141 | else 142 | BASEDIR := /lib/modules/$(shell uname -r) 143 | KERNELDIR ?= $(BASEDIR)/build 144 | PWD :=$(shell pwd) 145 | DRIVERDIR := $(shell find $(BASEDIR)/kernel/drivers/net/ethernet -name realtek -type d) 146 | ifeq ($(DRIVERDIR),) 147 | DRIVERDIR := $(shell find $(BASEDIR)/kernel/drivers/net -name realtek -type d) 148 | endif 149 | ifeq ($(DRIVERDIR),) 150 | DRIVERDIR := $(BASEDIR)/kernel/drivers/net 151 | endif 152 | RTKDIR := $(subst $(BASEDIR)/,,$(DRIVERDIR)) 153 | 154 | KERNEL_GCC_VERSION := $(shell cat /proc/version | sed -n 's/.*gcc version \([[:digit:]]\.[[:digit:]]\.[[:digit:]]\).*/\1/p') 155 | CCVERSION = $(shell $(CC) -dumpversion) 156 | 157 | KVER = $(shell uname -r) 158 | KMAJ = $(shell echo $(KVER) | \ 159 | sed -e 's/^\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*.*/\1/') 160 | KMIN = $(shell echo $(KVER) | \ 161 | sed -e 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*.*/\1/') 162 | KREV = $(shell echo $(KVER) | \ 163 | sed -e 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\).*/\1/') 164 | 165 | kver_ge = $(shell \ 166 | echo test | awk '{if($(KMAJ) < $(1)) {print 0} else { \ 167 | if($(KMAJ) > $(1)) {print 1} else { \ 168 | if($(KMIN) < $(2)) {print 0} else { \ 169 | if($(KMIN) > $(2)) {print 1} else { \ 170 | if($(KREV) < $(3)) {print 0} else { print 1 } \ 171 | }}}}}' \ 172 | ) 173 | 174 | .PHONY: all 175 | all: print_vars clean modules install 176 | 177 | print_vars: 178 | @echo 179 | @echo "CC: " $(CC) 180 | @echo "CCVERSION: " $(CCVERSION) 181 | @echo "KERNEL_GCC_VERSION: " $(KERNEL_GCC_VERSION) 182 | @echo "KVER: " $(KVER) 183 | @echo "KMAJ: " $(KMAJ) 184 | @echo "KMIN: " $(KMIN) 185 | @echo "KREV: " $(KREV) 186 | @echo "BASEDIR: " $(BASEDIR) 187 | @echo "DRIVERDIR: " $(DRIVERDIR) 188 | @echo "PWD: " $(PWD) 189 | @echo "RTKDIR: " $(RTKDIR) 190 | @echo 191 | 192 | .PHONY:modules 193 | modules: 194 | #ifeq ($(call kver_ge,5,0,0),1) 195 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 196 | #else 197 | # $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules 198 | #endif 199 | 200 | .PHONY:clean 201 | clean: 202 | #ifeq ($(call kver_ge,5,0,0),1) 203 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 204 | #else 205 | # $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean 206 | #endif 207 | 208 | .PHONY:install 209 | install: 210 | #ifeq ($(call kver_ge,5,0,0),1) 211 | $(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_DIR=$(RTKDIR) modules_install 212 | #else 213 | # $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) INSTALL_MOD_DIR=$(RTKDIR) modules_install 214 | #endif 215 | 216 | endif 217 | -------------------------------------------------------------------------------- /r8125_dash.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #ifndef _LINUX_R8125_DASH_H 36 | #define _LINUX_R8125_DASH_H 37 | 38 | #include 39 | 40 | #define SIOCDEVPRIVATE_RTLDASH SIOCDEVPRIVATE+2 41 | 42 | enum rtl_dash_cmd { 43 | RTL_DASH_ARP_NS_OFFLOAD = 0, 44 | RTL_DASH_SET_OOB_IPMAC, 45 | RTL_DASH_NOTIFY_OOB, 46 | 47 | RTL_DASH_SEND_BUFFER_DATA_TO_DASH_FW, 48 | RTL_DASH_CHECK_SEND_BUFFER_TO_DASH_FW_COMPLETE, 49 | RTL_DASH_GET_RCV_FROM_FW_BUFFER_DATA, 50 | RTL_DASH_OOB_REQ, 51 | RTL_DASH_OOB_ACK, 52 | RTL_DASH_DETACH_OOB_REQ, 53 | RTL_DASH_DETACH_OOB_ACK, 54 | 55 | RTL_FW_SET_IPV4 = 0x10, 56 | RTL_FW_GET_IPV4, 57 | RTL_FW_SET_IPV6, 58 | RTL_FW_GET_IPV6, 59 | RTL_FW_SET_EXT_SNMP, 60 | RTL_FW_GET_EXT_SNMP, 61 | RTL_FW_SET_WAKEUP_PATTERN, 62 | RTL_FW_GET_WAKEUP_PATTERN, 63 | RTL_FW_DEL_WAKEUP_PATTERN, 64 | 65 | RTLT_DASH_COMMAND_INVALID, 66 | }; 67 | 68 | struct rtl_dash_ip_mac { 69 | struct sockaddr ifru_addr; 70 | struct sockaddr ifru_netmask; 71 | struct sockaddr ifru_hwaddr; 72 | }; 73 | 74 | struct rtl_dash_ioctl_struct { 75 | __u32 cmd; 76 | __u32 offset; 77 | __u32 len; 78 | union { 79 | __u32 data; 80 | void *data_buffer; 81 | }; 82 | }; 83 | 84 | typedef struct _OSOOBHdr { 85 | __le32 len; 86 | u8 type; 87 | u8 flag; 88 | u8 hostReqV; 89 | u8 res; 90 | } 91 | OSOOBHdr, *POSOOBHdr; 92 | 93 | typedef struct _RX_DASH_BUFFER_TYPE_2 { 94 | OSOOBHdr oobhdr; 95 | u8 RxDataBuffer[0]; 96 | } 97 | RX_DASH_BUFFER_TYPE_2, *PRX_DASH_BUFFER_TYPE_2; 98 | 99 | #define ALIGN_8 (0x7) 100 | #define ALIGN_16 (0xf) 101 | #define ALIGN_32 (0x1f) 102 | #define ALIGN_64 (0x3f) 103 | #define ALIGN_256 (0xff) 104 | #define ALIGN_4096 (0xfff) 105 | 106 | #define OCP_REG_FIRMWARE_MAJOR_VERSION (0x120) 107 | 108 | #define HW_DASH_SUPPORT_DASH(_M) ((_M)->HwSuppDashVer > 0) 109 | #define HW_DASH_SUPPORT_TYPE_1(_M) ((_M)->HwSuppDashVer == 1) 110 | #define HW_DASH_SUPPORT_TYPE_2(_M) ((_M)->HwSuppDashVer == 2) 111 | #define HW_DASH_SUPPORT_TYPE_3(_M) ((_M)->HwSuppDashVer == 3) 112 | #define HW_DASH_SUPPORT_TYPE_4(_M) ((_M)->HwSuppDashVer == 4) 113 | #define HW_DASH_SUPPORT_CMAC(_M) (HW_DASH_SUPPORT_TYPE_2(_M) || HW_DASH_SUPPORT_TYPE_3(_M)) 114 | #define HW_DASH_SUPPORT_IPC2(_M) (HW_DASH_SUPPORT_TYPE_4(_M)) 115 | #define HW_DASH_SUPPORT_GET_FIRMWARE_VERSION(_M) (HW_DASH_SUPPORT_TYPE_2(_M) || \ 116 | HW_DASH_SUPPORT_TYPE_3(_M) || \ 117 | HW_DASH_SUPPORT_TYPE_4(_M)) 118 | 119 | #define RECV_FROM_FW_BUF_SIZE (1520) 120 | #define SEND_TO_FW_BUF_SIZE (1520) 121 | 122 | #define TXS_CC3_0 (BIT_0|BIT_1|BIT_2|BIT_3) 123 | #define TXS_EXC BIT_4 124 | #define TXS_LNKF BIT_5 125 | #define TXS_OWC BIT_6 126 | #define TXS_TES BIT_7 127 | #define TXS_UNF BIT_9 128 | #define TXS_LGSEN BIT_11 129 | #define TXS_LS BIT_12 130 | #define TXS_FS BIT_13 131 | #define TXS_EOR BIT_14 132 | #define TXS_OWN BIT_15 133 | 134 | #define TPPool_HRDY 0x20 135 | 136 | #define RXS_OWN BIT_15 137 | #define RXS_EOR BIT_14 138 | #define RXS_FS BIT_13 139 | #define RXS_LS BIT_12 140 | 141 | #define ISRIMR_DASH_INTR_EN BIT_12 142 | 143 | #define NO_BASE_ADDRESS 0x00000000 144 | 145 | /* IB2SOC registers */ 146 | #define IPC2_SWISR_DRIVER_READY 0x05 147 | #define IPC2_SWISR_DRIVER_EXIT 0x06 148 | #define IPC2_SWISR_CLIENTTOOL_SYNC_HOSTNAME 0x20 149 | #define IPC2_SWISR_DIS_DASH 0x55 150 | #define IPC2_SWISR_EN_DASH 0x56 151 | 152 | #define IPC2_IB2SOC_SET 0x10 153 | #define IPC2_IB2SOC_DATA 0x14 154 | #define IPC2_IB2SOC_CMD 0x18 155 | #define IPC2_IB2SOC_IMR 0x1C 156 | 157 | /* IPC2 registers */ 158 | #define IPC2_PCIE_BASE 0xC100 159 | #define IPC2_TX_SET_REG IPC2_PCIE_BASE 160 | #define IPC2_TX_STATUS_REG (IPC2_PCIE_BASE+0x04) 161 | #define IPC2_RX_STATUS_REG (IPC2_PCIE_BASE+0x08) 162 | #define IPC2_RX_CLEAR_REG (IPC2_PCIE_BASE+0x0C) 163 | #define IPC2_DATA_BASE 0x32000 164 | #define IPC2_BUFFER_LENGTH 0x1000 165 | #define IPC2_DATA_MASTER IPC2_DATA_BASE //dash tx buffer base 166 | #define IPC2_DATA_SLAVE (IPC2_DATA_BASE+IPC2_BUFFER_LENGTH) //dash rx buffer base 167 | #define IPC2_TX_BUFFER IPC2_DATA_MASTER 168 | #define IPC2_RX_BUFFER IPC2_DATA_SLAVE 169 | 170 | #define IPC2_TX_SEND_BIT BIT_0 171 | #define IPC2_TX_ACK_BIT BIT_8 172 | #define IPC2_RX_ROK_BIT BIT_0 173 | #define IPC2_RX_ACK_BIT BIT_8 174 | 175 | /* IPC2 write/read MMIO register */ 176 | #define RTL_DASH_IPC2_W8(tp, reg, val8) RTL_W8(tp, reg, val8) 177 | #define RTL_DASH_IPC2_W16(tp, reg, val16) RTL_W16(tp, reg, val16) 178 | #define RTL_DASH_IPC2_W32(tp, reg, val32) RTL_W32(tp, reg, val32) 179 | #define RTL_DASH_IPC2_R8(tp, reg) RTL_R8(tp, reg) 180 | #define RTL_DASH_IPC2_R16(tp, reg) RTL_R16(tp, reg) 181 | #define RTL_DASH_IPC2_R32(tp, reg) RTL_R32(tp, reg) 182 | 183 | /* DASH OOB Header Type */ 184 | #define DASH_OOB_HDR_TYPE_REQ 0x91 185 | #define DASH_OOB_HDR_TYPE_ACK 0x92 186 | 187 | struct rtl8125_private; 188 | 189 | int rtl8125_dash_ioctl(struct net_device *dev, struct ifreq *ifr); 190 | bool rtl8125_check_dash_interrupt(struct rtl8125_private *tp); 191 | void rtl8125_handle_dash_interrupt(struct net_device *dev); 192 | void rtl8125_clear_ipc2_isr(struct rtl8125_private *tp); 193 | void rtl8125_set_ipc2_soc_imr_bit(struct rtl8125_private *tp, u16 mask); 194 | void rtl8125_clear_ipc2_soc_imr_bit(struct rtl8125_private *tp, u16 mask); 195 | 196 | #endif /* _LINUX_R8125_DASH_H */ 197 | -------------------------------------------------------------------------------- /rtl_eeprom.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | 44 | #include "r8125.h" 45 | #include "rtl_eeprom.h" 46 | 47 | //------------------------------------------------------------------- 48 | //rtl8125_eeprom_type(): 49 | // tell the eeprom type 50 | //return value: 51 | // 0: the eeprom type is 93C46 52 | // 1: the eeprom type is 93C56 or 93C66 53 | //------------------------------------------------------------------- 54 | void rtl8125_eeprom_type(struct rtl8125_private *tp) 55 | { 56 | u16 magic = 0; 57 | 58 | if (tp->mcfg == CFG_METHOD_DEFAULT) 59 | goto out_no_eeprom; 60 | 61 | if(RTL_R8(tp, 0xD2)&0x04) { 62 | //not support 63 | //tp->eeprom_type = EEPROM_TWSI; 64 | //tp->eeprom_len = 256; 65 | goto out_no_eeprom; 66 | } else if(RTL_R32(tp, RxConfig) & RxCfg_9356SEL) { 67 | tp->eeprom_type = EEPROM_TYPE_93C56; 68 | tp->eeprom_len = 256; 69 | } else { 70 | tp->eeprom_type = EEPROM_TYPE_93C46; 71 | tp->eeprom_len = 128; 72 | } 73 | 74 | magic = rtl8125_eeprom_read_sc(tp, 0); 75 | 76 | out_no_eeprom: 77 | if ((magic != 0x8129) && (magic != 0x8128)) { 78 | tp->eeprom_type = EEPROM_TYPE_NONE; 79 | tp->eeprom_len = 0; 80 | } 81 | } 82 | 83 | void rtl8125_eeprom_cleanup(struct rtl8125_private *tp) 84 | { 85 | u8 x; 86 | 87 | x = RTL_R8(tp, Cfg9346); 88 | x &= ~(Cfg9346_EEDI | Cfg9346_EECS); 89 | 90 | RTL_W8(tp, Cfg9346, x); 91 | 92 | rtl8125_raise_clock(tp, &x); 93 | rtl8125_lower_clock(tp, &x); 94 | } 95 | 96 | static int rtl8125_eeprom_cmd_done(struct rtl8125_private *tp) 97 | { 98 | u8 x; 99 | int i; 100 | 101 | rtl8125_stand_by(tp); 102 | 103 | for (i = 0; i < 50000; i++) { 104 | x = RTL_R8(tp, Cfg9346); 105 | 106 | if (x & Cfg9346_EEDO) { 107 | udelay(RTL_CLOCK_RATE * 2 * 3); 108 | return 0; 109 | } 110 | udelay(1); 111 | } 112 | 113 | return -1; 114 | } 115 | 116 | //------------------------------------------------------------------- 117 | //rtl8125_eeprom_read_sc(): 118 | // read one word from eeprom 119 | //------------------------------------------------------------------- 120 | u16 rtl8125_eeprom_read_sc(struct rtl8125_private *tp, u16 reg) 121 | { 122 | int addr_sz = 6; 123 | u8 x; 124 | u16 data; 125 | 126 | if(tp->eeprom_type == EEPROM_TYPE_NONE) 127 | return -1; 128 | 129 | if (tp->eeprom_type==EEPROM_TYPE_93C46) 130 | addr_sz = 6; 131 | else if (tp->eeprom_type==EEPROM_TYPE_93C56) 132 | addr_sz = 8; 133 | 134 | x = Cfg9346_EEM1 | Cfg9346_EECS; 135 | RTL_W8(tp, Cfg9346, x); 136 | 137 | rtl8125_shift_out_bits(tp, RTL_EEPROM_READ_OPCODE, 3); 138 | rtl8125_shift_out_bits(tp, reg, addr_sz); 139 | 140 | data = rtl8125_shift_in_bits(tp); 141 | 142 | rtl8125_eeprom_cleanup(tp); 143 | 144 | RTL_W8(tp, Cfg9346, 0); 145 | 146 | return data; 147 | } 148 | 149 | //------------------------------------------------------------------- 150 | //rtl8125_eeprom_write_sc(): 151 | // write one word to a specific address in the eeprom 152 | //------------------------------------------------------------------- 153 | void rtl8125_eeprom_write_sc(struct rtl8125_private *tp, u16 reg, u16 data) 154 | { 155 | u8 x; 156 | int addr_sz = 6; 157 | int w_dummy_addr = 4; 158 | 159 | if(tp->eeprom_type == EEPROM_TYPE_NONE) 160 | return; 161 | 162 | if (tp->eeprom_type==EEPROM_TYPE_93C46) { 163 | addr_sz = 6; 164 | w_dummy_addr = 4; 165 | } else if (tp->eeprom_type==EEPROM_TYPE_93C56) { 166 | addr_sz = 8; 167 | w_dummy_addr = 6; 168 | } 169 | 170 | x = Cfg9346_EEM1 | Cfg9346_EECS; 171 | RTL_W8(tp, Cfg9346, x); 172 | 173 | rtl8125_shift_out_bits(tp, RTL_EEPROM_EWEN_OPCODE, 5); 174 | rtl8125_shift_out_bits(tp, reg, w_dummy_addr); 175 | rtl8125_stand_by(tp); 176 | 177 | rtl8125_shift_out_bits(tp, RTL_EEPROM_ERASE_OPCODE, 3); 178 | rtl8125_shift_out_bits(tp, reg, addr_sz); 179 | if (rtl8125_eeprom_cmd_done(tp) < 0) 180 | return; 181 | rtl8125_stand_by(tp); 182 | 183 | rtl8125_shift_out_bits(tp, RTL_EEPROM_WRITE_OPCODE, 3); 184 | rtl8125_shift_out_bits(tp, reg, addr_sz); 185 | rtl8125_shift_out_bits(tp, data, 16); 186 | if (rtl8125_eeprom_cmd_done(tp) < 0) 187 | return; 188 | rtl8125_stand_by(tp); 189 | 190 | rtl8125_shift_out_bits(tp, RTL_EEPROM_EWDS_OPCODE, 5); 191 | rtl8125_shift_out_bits(tp, reg, w_dummy_addr); 192 | 193 | rtl8125_eeprom_cleanup(tp); 194 | RTL_W8(tp, Cfg9346, 0); 195 | } 196 | 197 | void rtl8125_raise_clock(struct rtl8125_private *tp, u8 *x) 198 | { 199 | *x = *x | Cfg9346_EESK; 200 | RTL_W8(tp, Cfg9346, *x); 201 | udelay(RTL_CLOCK_RATE); 202 | } 203 | 204 | void rtl8125_lower_clock(struct rtl8125_private *tp, u8 *x) 205 | { 206 | *x = *x & ~Cfg9346_EESK; 207 | RTL_W8(tp, Cfg9346, *x); 208 | udelay(RTL_CLOCK_RATE); 209 | } 210 | 211 | void rtl8125_shift_out_bits(struct rtl8125_private *tp, int data, int count) 212 | { 213 | u8 x; 214 | int mask; 215 | 216 | mask = 0x01 << (count - 1); 217 | x = RTL_R8(tp, Cfg9346); 218 | x &= ~(Cfg9346_EEDI | Cfg9346_EEDO); 219 | 220 | do { 221 | if (data & mask) 222 | x |= Cfg9346_EEDI; 223 | else 224 | x &= ~Cfg9346_EEDI; 225 | 226 | RTL_W8(tp, Cfg9346, x); 227 | udelay(RTL_CLOCK_RATE); 228 | rtl8125_raise_clock(tp, &x); 229 | rtl8125_lower_clock(tp, &x); 230 | mask = mask >> 1; 231 | } while(mask); 232 | 233 | x &= ~Cfg9346_EEDI; 234 | RTL_W8(tp, Cfg9346, x); 235 | } 236 | 237 | u16 rtl8125_shift_in_bits(struct rtl8125_private *tp) 238 | { 239 | u8 x; 240 | u16 d, i; 241 | 242 | x = RTL_R8(tp, Cfg9346); 243 | x &= ~(Cfg9346_EEDI | Cfg9346_EEDO); 244 | 245 | d = 0; 246 | 247 | for (i = 0; i < 16; i++) { 248 | d = d << 1; 249 | rtl8125_raise_clock(tp, &x); 250 | 251 | x = RTL_R8(tp, Cfg9346); 252 | x &= ~Cfg9346_EEDI; 253 | 254 | if (x & Cfg9346_EEDO) 255 | d |= 1; 256 | 257 | rtl8125_lower_clock(tp, &x); 258 | } 259 | 260 | return d; 261 | } 262 | 263 | void rtl8125_stand_by(struct rtl8125_private *tp) 264 | { 265 | u8 x; 266 | 267 | x = RTL_R8(tp, Cfg9346); 268 | x &= ~(Cfg9346_EECS | Cfg9346_EESK); 269 | RTL_W8(tp, Cfg9346, x); 270 | udelay(RTL_CLOCK_RATE); 271 | 272 | x |= Cfg9346_EECS; 273 | RTL_W8(tp, Cfg9346, x); 274 | } 275 | 276 | void rtl8125_set_eeprom_sel_low(struct rtl8125_private *tp) 277 | { 278 | RTL_W8(tp, Cfg9346, Cfg9346_EEM1); 279 | RTL_W8(tp, Cfg9346, Cfg9346_EEM1 | Cfg9346_EESK); 280 | 281 | udelay(20); 282 | 283 | RTL_W8(tp, Cfg9346, Cfg9346_EEM1); 284 | } 285 | -------------------------------------------------------------------------------- /r8125_firmware.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "r8125_firmware.h" 40 | 41 | enum rtl_fw_opcode { 42 | PHY_READ = 0x0, 43 | PHY_DATA_OR = 0x1, 44 | PHY_DATA_AND = 0x2, 45 | PHY_BJMPN = 0x3, 46 | PHY_MDIO_CHG = 0x4, 47 | PHY_CLEAR_READCOUNT = 0x7, 48 | PHY_WRITE = 0x8, 49 | PHY_READCOUNT_EQ_SKIP = 0x9, 50 | PHY_COMP_EQ_SKIPN = 0xa, 51 | PHY_COMP_NEQ_SKIPN = 0xb, 52 | PHY_WRITE_PREVIOUS = 0xc, 53 | PHY_SKIPN = 0xd, 54 | PHY_DELAY_MS = 0xe, 55 | }; 56 | 57 | struct fw_info { 58 | u32 magic; 59 | char version[RTL8125_VER_SIZE]; 60 | __le32 fw_start; 61 | __le32 fw_len; 62 | u8 chksum; 63 | } __packed; 64 | 65 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,16,0) 66 | #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) 67 | #endif 68 | #define FW_OPCODE_SIZE sizeof_field(struct rtl8125_fw_phy_action, code[0]) 69 | 70 | static bool rtl8125_fw_format_ok(struct rtl8125_fw *rtl_fw) 71 | { 72 | const struct firmware *fw = rtl_fw->fw; 73 | struct fw_info *fw_info = (struct fw_info *)fw->data; 74 | struct rtl8125_fw_phy_action *pa = &rtl_fw->phy_action; 75 | 76 | if (fw->size < FW_OPCODE_SIZE) 77 | return false; 78 | 79 | if (!fw_info->magic) { 80 | size_t i, size, start; 81 | u8 checksum = 0; 82 | 83 | if (fw->size < sizeof(*fw_info)) 84 | return false; 85 | 86 | for (i = 0; i < fw->size; i++) 87 | checksum += fw->data[i]; 88 | if (checksum != 0) 89 | return false; 90 | 91 | start = le32_to_cpu(fw_info->fw_start); 92 | if (start > fw->size) 93 | return false; 94 | 95 | size = le32_to_cpu(fw_info->fw_len); 96 | if (size > (fw->size - start) / FW_OPCODE_SIZE) 97 | return false; 98 | 99 | strscpy(rtl_fw->version, fw_info->version, RTL8125_VER_SIZE); 100 | 101 | pa->code = (__le32 *)(fw->data + start); 102 | pa->size = size; 103 | } else { 104 | if (fw->size % FW_OPCODE_SIZE) 105 | return false; 106 | 107 | strscpy(rtl_fw->version, rtl_fw->fw_name, RTL8125_VER_SIZE); 108 | 109 | pa->code = (__le32 *)fw->data; 110 | pa->size = fw->size / FW_OPCODE_SIZE; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | static bool rtl8125_fw_data_ok(struct rtl8125_fw *rtl_fw) 117 | { 118 | struct rtl8125_fw_phy_action *pa = &rtl_fw->phy_action; 119 | size_t index; 120 | 121 | for (index = 0; index < pa->size; index++) { 122 | u32 action = le32_to_cpu(pa->code[index]); 123 | u32 val = action & 0x0000ffff; 124 | u32 regno = (action & 0x0fff0000) >> 16; 125 | 126 | switch (action >> 28) { 127 | case PHY_READ: 128 | case PHY_DATA_OR: 129 | case PHY_DATA_AND: 130 | case PHY_CLEAR_READCOUNT: 131 | case PHY_WRITE: 132 | case PHY_WRITE_PREVIOUS: 133 | case PHY_DELAY_MS: 134 | break; 135 | 136 | case PHY_MDIO_CHG: 137 | if (val > 1) 138 | goto out; 139 | break; 140 | 141 | case PHY_BJMPN: 142 | if (regno > index) 143 | goto out; 144 | break; 145 | case PHY_READCOUNT_EQ_SKIP: 146 | if (index + 2 >= pa->size) 147 | goto out; 148 | break; 149 | case PHY_COMP_EQ_SKIPN: 150 | case PHY_COMP_NEQ_SKIPN: 151 | case PHY_SKIPN: 152 | if (index + 1 + regno >= pa->size) 153 | goto out; 154 | break; 155 | 156 | default: 157 | dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action); 158 | return false; 159 | } 160 | } 161 | 162 | return true; 163 | out: 164 | dev_err(rtl_fw->dev, "Out of range of firmware\n"); 165 | return false; 166 | } 167 | 168 | void rtl8125_fw_write_firmware(struct rtl8125_private *tp, struct rtl8125_fw *rtl_fw) 169 | { 170 | struct rtl8125_fw_phy_action *pa = &rtl_fw->phy_action; 171 | rtl8125_fw_write_t fw_write = rtl_fw->phy_write; 172 | rtl8125_fw_read_t fw_read = rtl_fw->phy_read; 173 | int predata = 0, count = 0; 174 | size_t index; 175 | 176 | for (index = 0; index < pa->size; index++) { 177 | u32 action = le32_to_cpu(pa->code[index]); 178 | u32 data = action & 0x0000ffff; 179 | u32 regno = (action & 0x0fff0000) >> 16; 180 | enum rtl_fw_opcode opcode = action >> 28; 181 | 182 | if (!action) 183 | break; 184 | 185 | switch (opcode) { 186 | case PHY_READ: 187 | predata = fw_read(tp, regno); 188 | count++; 189 | break; 190 | case PHY_DATA_OR: 191 | predata |= data; 192 | break; 193 | case PHY_DATA_AND: 194 | predata &= data; 195 | break; 196 | case PHY_BJMPN: 197 | index -= (regno + 1); 198 | break; 199 | case PHY_MDIO_CHG: 200 | if (data) { 201 | fw_write = rtl_fw->mac_mcu_write; 202 | fw_read = rtl_fw->mac_mcu_read; 203 | } else { 204 | fw_write = rtl_fw->phy_write; 205 | fw_read = rtl_fw->phy_read; 206 | } 207 | 208 | break; 209 | case PHY_CLEAR_READCOUNT: 210 | count = 0; 211 | break; 212 | case PHY_WRITE: 213 | fw_write(tp, regno, data); 214 | break; 215 | case PHY_READCOUNT_EQ_SKIP: 216 | if (count == data) 217 | index++; 218 | break; 219 | case PHY_COMP_EQ_SKIPN: 220 | if (predata == data) 221 | index += regno; 222 | break; 223 | case PHY_COMP_NEQ_SKIPN: 224 | if (predata != data) 225 | index += regno; 226 | break; 227 | case PHY_WRITE_PREVIOUS: 228 | fw_write(tp, regno, predata); 229 | break; 230 | case PHY_SKIPN: 231 | index += regno; 232 | break; 233 | case PHY_DELAY_MS: 234 | mdelay(1 * data); 235 | break; 236 | } 237 | } 238 | } 239 | 240 | void rtl8125_fw_release_firmware(struct rtl8125_fw *rtl_fw) 241 | { 242 | release_firmware(rtl_fw->fw); 243 | } 244 | 245 | int rtl8125_fw_request_firmware(struct rtl8125_fw *rtl_fw) 246 | { 247 | int rc; 248 | 249 | rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); 250 | if (rc < 0) 251 | goto out; 252 | 253 | if (!rtl8125_fw_format_ok(rtl_fw) || !rtl8125_fw_data_ok(rtl_fw)) { 254 | release_firmware(rtl_fw->fw); 255 | rc = -EINVAL; 256 | goto out; 257 | } 258 | 259 | return 0; 260 | out: 261 | dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n", 262 | rtl_fw->fw_name, rc); 263 | return rc; 264 | } 265 | -------------------------------------------------------------------------------- /rtltool.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include "r8125.h" 44 | #include "rtl_eeprom.h" 45 | #include "rtltool.h" 46 | 47 | int rtl8125_tool_ioctl(struct rtl8125_private *tp, struct ifreq *ifr) 48 | { 49 | struct rtltool_cmd my_cmd; 50 | unsigned long flags; 51 | int ret; 52 | 53 | if (copy_from_user(&my_cmd, ifr->ifr_data, sizeof(my_cmd))) 54 | return -EFAULT; 55 | 56 | ret = 0; 57 | switch (my_cmd.cmd) { 58 | case RTLTOOL_READ_MAC: 59 | if ((my_cmd.offset + my_cmd.len) > pci_resource_len(tp->pci_dev, 2)) { 60 | ret = -EINVAL; 61 | break; 62 | } 63 | 64 | if (my_cmd.len==1) 65 | my_cmd.data = readb(tp->mmio_addr+my_cmd.offset); 66 | else if (my_cmd.len==2) 67 | my_cmd.data = readw(tp->mmio_addr+(my_cmd.offset&~1)); 68 | else if (my_cmd.len==4) 69 | my_cmd.data = readl(tp->mmio_addr+(my_cmd.offset&~3)); 70 | else { 71 | ret = -EOPNOTSUPP; 72 | break; 73 | } 74 | 75 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 76 | ret = -EFAULT; 77 | break; 78 | } 79 | break; 80 | 81 | case RTLTOOL_WRITE_MAC: 82 | if ((my_cmd.offset + my_cmd.len) > pci_resource_len(tp->pci_dev, 2)) { 83 | ret = -EINVAL; 84 | break; 85 | } 86 | 87 | if (my_cmd.len==1) 88 | writeb(my_cmd.data, tp->mmio_addr+my_cmd.offset); 89 | else if (my_cmd.len==2) 90 | writew(my_cmd.data, tp->mmio_addr+(my_cmd.offset&~1)); 91 | else if (my_cmd.len==4) 92 | writel(my_cmd.data, tp->mmio_addr+(my_cmd.offset&~3)); 93 | else { 94 | ret = -EOPNOTSUPP; 95 | break; 96 | } 97 | 98 | break; 99 | 100 | case RTLTOOL_READ_PHY: 101 | r8125_spin_lock(&tp->phy_lock, flags); 102 | my_cmd.data = rtl8125_mdio_prot_read(tp, my_cmd.offset); 103 | r8125_spin_unlock(&tp->phy_lock, flags); 104 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 105 | ret = -EFAULT; 106 | break; 107 | } 108 | 109 | break; 110 | 111 | case RTLTOOL_WRITE_PHY: 112 | r8125_spin_lock(&tp->phy_lock, flags); 113 | rtl8125_mdio_prot_write(tp, my_cmd.offset, my_cmd.data); 114 | r8125_spin_unlock(&tp->phy_lock, flags); 115 | break; 116 | 117 | case RTLTOOL_READ_EPHY: 118 | my_cmd.data = rtl8125_ephy_read(tp, my_cmd.offset); 119 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 120 | ret = -EFAULT; 121 | break; 122 | } 123 | 124 | break; 125 | 126 | case RTLTOOL_WRITE_EPHY: 127 | rtl8125_ephy_write(tp, my_cmd.offset, my_cmd.data); 128 | break; 129 | 130 | case RTLTOOL_READ_ERI: 131 | my_cmd.data = 0; 132 | if (my_cmd.len==1 || my_cmd.len==2 || my_cmd.len==4) { 133 | my_cmd.data = rtl8125_eri_read(tp, my_cmd.offset, my_cmd.len, ERIAR_ExGMAC); 134 | } else { 135 | ret = -EOPNOTSUPP; 136 | break; 137 | } 138 | 139 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 140 | ret = -EFAULT; 141 | break; 142 | } 143 | 144 | break; 145 | 146 | case RTLTOOL_WRITE_ERI: 147 | if (my_cmd.len==1 || my_cmd.len==2 || my_cmd.len==4) { 148 | rtl8125_eri_write(tp, my_cmd.offset, my_cmd.len, my_cmd.data, ERIAR_ExGMAC); 149 | } else { 150 | ret = -EOPNOTSUPP; 151 | break; 152 | } 153 | break; 154 | 155 | case RTLTOOL_READ_PCI: 156 | my_cmd.data = 0; 157 | if (my_cmd.len==1) 158 | pci_read_config_byte(tp->pci_dev, my_cmd.offset, 159 | (u8 *)&my_cmd.data); 160 | else if (my_cmd.len==2) 161 | pci_read_config_word(tp->pci_dev, my_cmd.offset, 162 | (u16 *)&my_cmd.data); 163 | else if (my_cmd.len==4) 164 | pci_read_config_dword(tp->pci_dev, my_cmd.offset, 165 | &my_cmd.data); 166 | else { 167 | ret = -EOPNOTSUPP; 168 | break; 169 | } 170 | 171 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 172 | ret = -EFAULT; 173 | break; 174 | } 175 | break; 176 | 177 | case RTLTOOL_WRITE_PCI: 178 | if (my_cmd.len==1) 179 | pci_write_config_byte(tp->pci_dev, my_cmd.offset, 180 | my_cmd.data); 181 | else if (my_cmd.len==2) 182 | pci_write_config_word(tp->pci_dev, my_cmd.offset, 183 | my_cmd.data); 184 | else if (my_cmd.len==4) 185 | pci_write_config_dword(tp->pci_dev, my_cmd.offset, 186 | my_cmd.data); 187 | else { 188 | ret = -EOPNOTSUPP; 189 | break; 190 | } 191 | 192 | break; 193 | 194 | case RTLTOOL_READ_EEPROM: 195 | my_cmd.data = rtl8125_eeprom_read_sc(tp, my_cmd.offset); 196 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 197 | ret = -EFAULT; 198 | break; 199 | } 200 | 201 | break; 202 | 203 | case RTLTOOL_WRITE_EEPROM: 204 | rtl8125_eeprom_write_sc(tp, my_cmd.offset, my_cmd.data); 205 | break; 206 | 207 | case RTL_READ_OOB_MAC: 208 | rtl8125_oob_mutex_lock(tp); 209 | my_cmd.data = rtl8125_ocp_read(tp, my_cmd.offset, 4); 210 | rtl8125_oob_mutex_unlock(tp); 211 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 212 | ret = -EFAULT; 213 | break; 214 | } 215 | break; 216 | 217 | case RTL_WRITE_OOB_MAC: 218 | if (my_cmd.len == 0 || my_cmd.len > 4) 219 | return -EOPNOTSUPP; 220 | 221 | rtl8125_oob_mutex_lock(tp); 222 | rtl8125_ocp_write(tp, my_cmd.offset, my_cmd.len, my_cmd.data); 223 | rtl8125_oob_mutex_unlock(tp); 224 | break; 225 | 226 | case RTL_ENABLE_PCI_DIAG: 227 | r8125_spin_lock(&tp->phy_lock, flags); 228 | tp->rtk_enable_diag = 1; 229 | r8125_spin_unlock(&tp->phy_lock, flags); 230 | 231 | dprintk("enable rtk diag\n"); 232 | break; 233 | 234 | case RTL_DISABLE_PCI_DIAG: 235 | r8125_spin_lock(&tp->phy_lock, flags); 236 | tp->rtk_enable_diag = 0; 237 | r8125_spin_unlock(&tp->phy_lock, flags); 238 | 239 | dprintk("disable rtk diag\n"); 240 | break; 241 | 242 | case RTL_READ_MAC_OCP: 243 | if (my_cmd.offset % 2) 244 | return -EOPNOTSUPP; 245 | 246 | my_cmd.data = rtl8125_mac_ocp_read(tp, my_cmd.offset); 247 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 248 | ret = -EFAULT; 249 | break; 250 | } 251 | break; 252 | 253 | case RTL_WRITE_MAC_OCP: 254 | if ((my_cmd.offset % 2) || (my_cmd.len != 2)) 255 | return -EOPNOTSUPP; 256 | 257 | rtl8125_mac_ocp_write(tp, my_cmd.offset, (u16)my_cmd.data); 258 | break; 259 | 260 | case RTL_DIRECT_READ_PHY_OCP: 261 | r8125_spin_lock(&tp->phy_lock, flags); 262 | my_cmd.data = rtl8125_mdio_prot_direct_read_phy_ocp(tp, my_cmd.offset); 263 | r8125_spin_unlock(&tp->phy_lock, flags); 264 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 265 | ret = -EFAULT; 266 | break; 267 | } 268 | 269 | break; 270 | 271 | case RTL_DIRECT_WRITE_PHY_OCP: 272 | r8125_spin_lock(&tp->phy_lock, flags); 273 | rtl8125_mdio_prot_direct_write_phy_ocp(tp, my_cmd.offset, my_cmd.data); 274 | r8125_spin_unlock(&tp->phy_lock, flags); 275 | break; 276 | 277 | #ifdef ENABLE_FIBER_SUPPORT 278 | case RTL_READ_FIBER_PHY: 279 | if (!HW_FIBER_STATUS_CONNECTED(tp)) { 280 | ret = -EOPNOTSUPP; 281 | break; 282 | } 283 | 284 | r8125_spin_lock(&tp->phy_lock, flags); 285 | my_cmd.data = rtl8125_fiber_mdio_read(tp, my_cmd.offset); 286 | r8125_spin_unlock(&tp->phy_lock, flags); 287 | if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { 288 | ret = -EFAULT; 289 | break; 290 | } 291 | 292 | break; 293 | 294 | case RTL_WRITE_FIBER_PHY: 295 | if (!HW_FIBER_STATUS_CONNECTED(tp)) { 296 | ret = -EOPNOTSUPP; 297 | break; 298 | } 299 | 300 | r8125_spin_lock(&tp->phy_lock, flags); 301 | rtl8125_fiber_mdio_write(tp, my_cmd.offset, my_cmd.data); 302 | r8125_spin_unlock(&tp->phy_lock, flags); 303 | break; 304 | #endif /* ENABLE_FIBER_SUPPORT */ 305 | 306 | default: 307 | ret = -EOPNOTSUPP; 308 | break; 309 | } 310 | 311 | return ret; 312 | } 313 | -------------------------------------------------------------------------------- /r8125_fiber.c: -------------------------------------------------------------------------------- 1 | /* 2 | ################################################################################ 3 | # 4 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 5 | # controllers with PCI-Express interface. 6 | # 7 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 8 | # 9 | # This program is free software; you can redistribute it and/or modify it 10 | # under the terms of the GNU General Public License as published by the Free 11 | # Software Foundation; either version 2 of the License, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | # more details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program; if not, see . 21 | # 22 | # Author: 23 | # Realtek NIC software team 24 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 25 | # 26 | ################################################################################ 27 | */ 28 | 29 | /************************************************************************************ 30 | * This product is covered by one or more of the following patents: 31 | * US6,570,884, US6,115,776, and US6,327,625. 32 | ***********************************************************************************/ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #include "r8125.h" 39 | #include "r8125_fiber.h" 40 | 41 | static void 42 | rtl8125_fiber_set_mdc_gpio_c45(struct rtl8125_private *tp, bool pu) 43 | { 44 | if (pu) 45 | rtl8125_set_mac_ocp_bit(tp, 0xDC52, BIT_7); 46 | else 47 | rtl8125_clear_mac_ocp_bit(tp, 0xDC52, BIT_7); 48 | 49 | //RtPciCommittp); 50 | } 51 | 52 | static void 53 | rtl8125_fiber_set_mdc(struct rtl8125_private *tp, bool pu) 54 | { 55 | rtl8125_fiber_set_mdc_gpio_c45(tp, pu); 56 | } 57 | 58 | static void 59 | rtl8125_fiber_set_mdcDownUp(struct rtl8125_private *tp) 60 | { 61 | udelay(1); 62 | rtl8125_fiber_set_mdc(tp, 0); 63 | udelay(1); 64 | rtl8125_fiber_set_mdc(tp, 1); 65 | } 66 | 67 | static void 68 | rtl8125_fiber_set_mdio_bit_gpio_c45(struct rtl8125_private *tp, bool pu) 69 | { 70 | if (pu) 71 | rtl8125_set_mac_ocp_bit(tp, 0xDC52, BIT_2); 72 | else 73 | rtl8125_clear_mac_ocp_bit(tp, 0xDC52, BIT_2); 74 | 75 | //RtPciCommittp); 76 | 77 | rtl8125_fiber_set_mdcDownUp(tp); 78 | } 79 | 80 | static void 81 | rtl8125_fiber_set_mdio_bit(struct rtl8125_private *tp, bool pu) 82 | { 83 | rtl8125_fiber_set_mdio_bit_gpio_c45(tp, pu); 84 | } 85 | 86 | static u16 87 | rtl8125_fiber_get_mdio_bit_gpio_c45(struct rtl8125_private *tp) 88 | { 89 | rtl8125_fiber_set_mdcDownUp(tp); 90 | 91 | return !!(rtl8125_mac_ocp_read(tp, 0xDC58) & BIT(2)); 92 | } 93 | 94 | static u16 95 | rtl8125_fiber_get_mdio_bit(struct rtl8125_private *tp) 96 | { 97 | return rtl8125_fiber_get_mdio_bit_gpio_c45(tp); 98 | } 99 | 100 | static void 101 | rtl8125_fiber_shift_bit_in(struct rtl8125_private *tp, u32 val, int count) 102 | { 103 | int i; 104 | 105 | for (i = (count - 1); i >= 0; i--) 106 | rtl8125_fiber_set_mdio_bit(tp, !!(val & BIT(i))); 107 | } 108 | 109 | static u16 110 | rtl8125_fiber_shift_bit_out(struct rtl8125_private *tp) 111 | { 112 | u16 data = 0; 113 | int i; 114 | 115 | for (i = 15; i >= 0; i--) 116 | data += (rtl8125_fiber_get_mdio_bit(tp) << i); 117 | 118 | return data; 119 | } 120 | 121 | static void 122 | rtl8125_fiber_dir_gpio_c45(struct rtl8125_private *tp, bool output_mode) 123 | { 124 | if (output_mode) 125 | rtl8125_set_mac_ocp_bit(tp, 0xDC4C, BIT_2); 126 | else 127 | rtl8125_clear_mac_ocp_bit(tp, 0xDC4C, BIT_2); 128 | } 129 | 130 | static void 131 | rtl8125_fiber_dir(struct rtl8125_private *tp, bool output_mode) 132 | { 133 | rtl8125_fiber_dir_gpio_c45(tp, output_mode); 134 | } 135 | 136 | //fiber 137 | #define R8125_FIBER_C22 (0) 138 | #define R8125_FIBER_C45 (1) 139 | 140 | // sfp opcodes 141 | #define R8125_FIBER_ST (1) 142 | #define R8125_FIBER_OP_W (1) 143 | #define R8125_FIBER_OP_R (2) 144 | #define R8125_FIBER_TA (2) 145 | 146 | // sfp C45 opcodes 147 | #define R8125_FIBER_MDIO_C45 (BIT(15)) 148 | #define R8125_FIBER_C45_ST (R8125_FIBER_MDIO_C45 | 0) 149 | #define R8125_FIBER_C45_OP_ADDR (R8125_FIBER_MDIO_C45 | 0) 150 | #define R8125_FIBER_C45_OP_W (R8125_FIBER_MDIO_C45 | 1) 151 | #define R8125_FIBER_C45_OP_R (R8125_FIBER_MDIO_C45 | 3) 152 | 153 | static void 154 | rtl8125_fiber_cmd(struct rtl8125_private *tp, u32 cmd, u8 phy_addr, 155 | u32 reg) 156 | { 157 | /* change to output mode */ 158 | rtl8125_fiber_dir(tp, 1); 159 | 160 | /* preamble 32bit of 1 */ 161 | rtl8125_fiber_shift_bit_in(tp, UINT_MAX, 32); 162 | 163 | /* start bit */ 164 | if (cmd & R8125_FIBER_MDIO_C45) 165 | rtl8125_fiber_shift_bit_in(tp, R8125_FIBER_C45_ST, 2); 166 | else 167 | rtl8125_fiber_shift_bit_in(tp, R8125_FIBER_ST, 2); 168 | 169 | /* opcode */ 170 | rtl8125_fiber_shift_bit_in(tp, cmd, 2); 171 | 172 | /* phy address */ 173 | rtl8125_fiber_shift_bit_in(tp, phy_addr, 5); 174 | 175 | /* phy reg */ 176 | rtl8125_fiber_shift_bit_in(tp, reg, 5); 177 | } 178 | 179 | static u8 180 | rtl8125_fiber_cmdAddr(struct rtl8125_private *tp, u8 phy_addr, u32 reg) 181 | { 182 | u8 dev_addr = (reg >> 16) & 0x1F; 183 | u16 addr = (u16)reg; 184 | 185 | rtl8125_fiber_cmd(tp, R8125_FIBER_C45_OP_ADDR, phy_addr, dev_addr); 186 | 187 | /* turn-around(TA) */ 188 | rtl8125_fiber_shift_bit_in(tp, R8125_FIBER_TA, 2); 189 | 190 | rtl8125_fiber_shift_bit_in(tp, addr, 16); 191 | 192 | rtl8125_fiber_dir(tp, 0); 193 | 194 | rtl8125_fiber_get_mdio_bit(tp); 195 | 196 | return dev_addr; 197 | } 198 | 199 | static void 200 | rtl8125_fiber_reset_gpio_c45(struct rtl8125_private *tp) 201 | { 202 | rtl8125_set_mac_ocp_bit(tp, 0xDC4C, (BIT_7 | BIT_2)); 203 | 204 | /* init sfp interface */ 205 | rtl8125_clear_mac_ocp_bit(tp, 0xDC52, BIT_7); 206 | rtl8125_set_mac_ocp_bit(tp, 0xDC52, BIT_2); 207 | } 208 | 209 | static void 210 | rtl8125_fiber_write_common(struct rtl8125_private *tp, u16 val) 211 | { 212 | /* turn-around(TA) */ 213 | rtl8125_fiber_shift_bit_in(tp, R8125_FIBER_TA, 2); 214 | 215 | /* write phy data */ 216 | rtl8125_fiber_shift_bit_in(tp, val, 16); 217 | 218 | /* change to input mode */ 219 | rtl8125_fiber_dir(tp, 0); 220 | 221 | rtl8125_fiber_get_mdio_bit(tp); 222 | } 223 | 224 | static void 225 | rtl8125_fiber_mdio_write_gpio_c45( 226 | struct rtl8125_private *tp, 227 | u32 reg, 228 | u16 val, 229 | u8 phy_addr) 230 | { 231 | /* opcode write */ 232 | reg = rtl8125_fiber_cmdAddr(tp, phy_addr, reg); 233 | rtl8125_fiber_cmd(tp, R8125_FIBER_C45_OP_W, phy_addr, reg); 234 | 235 | rtl8125_fiber_write_common(tp, val); 236 | } 237 | 238 | static u16 239 | rtl8125_fiber_read_common(struct rtl8125_private *tp) 240 | { 241 | u16 data = 0; 242 | 243 | /* change to input mode */ 244 | rtl8125_fiber_dir(tp, 0); 245 | 246 | /* TA 0 */ 247 | rtl8125_fiber_get_mdio_bit(tp); 248 | 249 | /* read phy data */ 250 | data = rtl8125_fiber_shift_bit_out(tp); 251 | 252 | rtl8125_fiber_get_mdio_bit(tp); 253 | 254 | return data; 255 | } 256 | 257 | static u16 258 | rtl8125_fiber_mdio_read_gpio_c45( 259 | struct rtl8125_private *tp, 260 | u32 reg, 261 | u8 phy_addr) 262 | { 263 | reg = rtl8125_fiber_cmdAddr(tp, phy_addr, reg); 264 | rtl8125_fiber_cmd(tp, R8125_FIBER_C45_OP_R, phy_addr, reg); 265 | 266 | return rtl8125_fiber_read_common(tp); 267 | } 268 | 269 | void 270 | rtl8125_fiber_mdio_write( 271 | struct rtl8125_private *tp, 272 | u32 reg, 273 | u16 val) 274 | { 275 | switch(tp->HwFiberStat) { 276 | case FIBER_STAT_CONNECT_GPO_C45: 277 | return rtl8125_fiber_mdio_write_gpio_c45(tp, reg, val, 0); 278 | default: 279 | return; 280 | } 281 | } 282 | 283 | u16 284 | rtl8125_fiber_mdio_read( 285 | struct rtl8125_private *tp, 286 | u32 reg) 287 | { 288 | switch(tp->HwFiberStat) { 289 | case FIBER_STAT_CONNECT_GPO_C45: 290 | return rtl8125_fiber_mdio_read_gpio_c45(tp, reg, 0); 291 | default: 292 | return 0xffff; 293 | } 294 | } 295 | 296 | static void 297 | rtl8125_fiber_clear_and_set_phy_bit(struct rtl8125_private *tp, u32 addr, u16 clearmask, u16 setmask) 298 | { 299 | u16 PhyRegValue; 300 | 301 | PhyRegValue = rtl8125_fiber_mdio_read(tp, addr); 302 | PhyRegValue &= ~clearmask; 303 | PhyRegValue |= setmask; 304 | rtl8125_fiber_mdio_write(tp, addr, PhyRegValue); 305 | } 306 | 307 | static void 308 | rtl8125_fiber_clear_phy_bit(struct rtl8125_private *tp, u32 addr, u16 mask) 309 | { 310 | rtl8125_fiber_clear_and_set_phy_bit(tp, addr, mask, 0); 311 | } 312 | 313 | static void 314 | rtl8125_fiber_set_phy_bit(struct rtl8125_private *tp, u32 addr, u16 mask) 315 | { 316 | rtl8125_fiber_clear_and_set_phy_bit(tp, addr, 0, mask); 317 | } 318 | 319 | #define R8125_MAKE_C45_ADDR(_mmd, _addr) (_mmd << 16 | _addr) 320 | 321 | static void 322 | rtl8125_fiber_phy_reset_8221d(struct rtl8125_private *tp) 323 | { 324 | u16 PhyRegValue; 325 | u32 Timeout; 326 | 327 | rtl8125_fiber_set_phy_bit(tp, R8125_MAKE_C45_ADDR(0x01, 0x00), BIT_15); 328 | 329 | Timeout = 0; 330 | do { 331 | udelay(1000); 332 | 333 | PhyRegValue = rtl8125_fiber_mdio_read(tp, R8125_MAKE_C45_ADDR(0x01, 0x00)); 334 | 335 | Timeout++; 336 | } while ((PhyRegValue & BIT_15) && (Timeout < 20)); 337 | } 338 | 339 | static void 340 | rtl8125_fiber_phy_reset(struct rtl8125_private *tp) 341 | { 342 | switch (tp->HwFiberModeVer) { 343 | case FIBER_MODE_RTL8125D_RTL8221D: 344 | rtl8125_fiber_phy_reset_8221d(tp); 345 | break; 346 | } 347 | } 348 | 349 | static void 350 | rtl8125_fiber_set_rtl8221d_phy_mode(struct rtl8125_private *tp, u16 mode) 351 | { 352 | mode &= 0x3f; 353 | 354 | rtl8125_fiber_clear_phy_bit(tp, R8125_MAKE_C45_ADDR(30, 0x75F3), BIT_0); 355 | rtl8125_fiber_clear_and_set_phy_bit(tp, 356 | R8125_MAKE_C45_ADDR(30, 0x697A), 357 | 0x003F, 358 | mode); 359 | } 360 | 361 | static void 362 | rtl8125_fiber_set_phy_mode(struct rtl8125_private *tp, u16 mode) 363 | { 364 | switch (tp->HwFiberModeVer) { 365 | case FIBER_MODE_RTL8125D_RTL8221D: 366 | rtl8125_fiber_set_rtl8221d_phy_mode(tp, mode); 367 | break; 368 | default: 369 | break; 370 | } 371 | } 372 | 373 | static void 374 | rtl8125_hw_rtl8221d_phy_config(struct rtl8125_private *tp) 375 | { 376 | rtl8125_fiber_reset_gpio_c45(tp); 377 | 378 | rtl8125_fiber_set_phy_mode(tp, (tp->speed == SPEED_2500) ? 0x02 : 0x04); 379 | 380 | 381 | rtl8125_fiber_clear_phy_bit(tp, R8125_MAKE_C45_ADDR(0x07, 0x3C), (BIT_2 | BIT_1)); 382 | rtl8125_fiber_clear_phy_bit(tp, R8125_MAKE_C45_ADDR(0x07, 0x3E), (BIT_1 | BIT_0)); 383 | 384 | 385 | rtl8125_fiber_phy_reset(tp); 386 | } 387 | 388 | void 389 | rtl8125_hw_fiber_phy_config(struct rtl8125_private *tp) 390 | { 391 | switch (tp->HwFiberModeVer) { 392 | case FIBER_MODE_RTL8125D_RTL8221D: 393 | rtl8125_hw_rtl8221d_phy_config(tp); 394 | break; 395 | default: 396 | break; 397 | } 398 | } 399 | 400 | #define RTL8221D_PHY_ID_1 0x001C 401 | #define RTL8221D_PHY_ID_2 0xC849 402 | static u32 403 | rtl8125_fiber_get_connect_status_8221d(struct rtl8125_private *tp) 404 | { 405 | int i; 406 | int const checkcnt = 4; 407 | 408 | rtl8125_fiber_reset_gpio_c45(tp); 409 | 410 | for (i = 0; i < checkcnt; i++) { 411 | if (RTL8221D_PHY_ID_1 != rtl8125_fiber_mdio_read_gpio_c45(tp, R8125_MAKE_C45_ADDR(0x01, 0x02), 0) || 412 | RTL8221D_PHY_ID_2 != rtl8125_fiber_mdio_read_gpio_c45(tp, R8125_MAKE_C45_ADDR(0x01, 0x03), 0)) 413 | return FIBER_STAT_DISCONNECT; 414 | } 415 | 416 | return FIBER_STAT_CONNECT_GPO_C45; 417 | } 418 | 419 | static u32 420 | rtl8125_fiber_get_connect_status(struct rtl8125_private *tp) 421 | { 422 | switch (tp->HwFiberModeVer) { 423 | case FIBER_MODE_RTL8125D_RTL8221D: 424 | return rtl8125_fiber_get_connect_status_8221d(tp); 425 | default: 426 | return FIBER_STAT_NOT_CHECKED; 427 | } 428 | } 429 | 430 | void 431 | rtl8125_check_fiber_mode_support(struct rtl8125_private *tp) 432 | { 433 | switch(tp->mcfg) { 434 | case CFG_METHOD_10: 435 | case CFG_METHOD_11: { 436 | u8 tmp = (u8)rtl8125_mac_ocp_read(tp, 0xD006); 437 | if (tmp == 0x03) 438 | tp->HwFiberModeVer = FIBER_MODE_RTL8125D_RTL8221D; 439 | } 440 | break; 441 | } 442 | 443 | if (HW_FIBER_MODE_ENABLED(tp)) 444 | tp->HwFiberStat = rtl8125_fiber_get_connect_status(tp); 445 | } 446 | 447 | unsigned int 448 | rtl8125_fiber_link_ok(struct net_device *dev) 449 | { 450 | struct rtl8125_private *tp = netdev_priv(dev); 451 | u16 status; 452 | 453 | switch (tp->HwFiberStat) { 454 | case FIBER_STAT_CONNECT_GPO_C45: 455 | status = rtl8125_fiber_mdio_read(tp, R8125_MAKE_C45_ADDR(30, 0x758D)); 456 | if (status != USHRT_MAX && status & BIT_1) 457 | return 1; 458 | else 459 | return 0; 460 | break; 461 | default: 462 | return 0; 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /r8125_dash.c: -------------------------------------------------------------------------------- 1 | /* 2 | ################################################################################ 3 | # 4 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 5 | # controllers with PCI-Express interface. 6 | # 7 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 8 | # 9 | # This program is free software; you can redistribute it and/or modify it 10 | # under the terms of the GNU General Public License as published by the Free 11 | # Software Foundation; either version 2 of the License, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | # more details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program; if not, see . 21 | # 22 | # Author: 23 | # Realtek NIC software team 24 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 25 | # 26 | ################################################################################ 27 | */ 28 | 29 | /************************************************************************************ 30 | * This product is covered by one or more of the following patents: 31 | * US6,570,884, US6,115,776, and US6,327,625. 32 | ***********************************************************************************/ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "r8125.h" 54 | #include "r8125_dash.h" 55 | #include "rtl_eeprom.h" 56 | 57 | static void r8125_dash_set_ipc2_reg_bit(struct rtl8125_private *tp, unsigned long reg, u32 mask) 58 | { 59 | RTL_DASH_IPC2_W32(tp, reg, RTL_DASH_IPC2_R32(tp, reg) | mask); 60 | } 61 | 62 | /* 63 | static void r8125_dash_clear_ipc2_reg_bit(struct rtl8125_private *tp, unsigned long reg, u32 mask) 64 | { 65 | RTL_DASH_IPC2_W32(tp, reg, RTL_DASH_IPC2_R32(tp, reg) & ~mask); 66 | } 67 | */ 68 | 69 | static void r8125_write_ipc2_tx_ack(struct rtl8125_private *tp) 70 | { 71 | if (!tp->DASH) 72 | return; 73 | 74 | if (!HW_DASH_SUPPORT_IPC2(tp)) 75 | return; 76 | 77 | r8125_dash_set_ipc2_reg_bit(tp, IPC2_TX_SET_REG, IPC2_TX_ACK_BIT); 78 | } 79 | 80 | static void r8125_write_ipc2_tx_polling(struct rtl8125_private *tp) 81 | { 82 | if (!tp->DASH) 83 | return; 84 | 85 | if (!HW_DASH_SUPPORT_IPC2(tp)) 86 | return; 87 | 88 | r8125_dash_set_ipc2_reg_bit(tp, IPC2_TX_SET_REG, IPC2_TX_SEND_BIT); 89 | } 90 | 91 | static unsigned long 92 | r8125_get_ipc2_rx_buffer(struct rtl8125_private *tp) 93 | { 94 | if (HW_DASH_SUPPORT_IPC2(tp)) 95 | return IPC2_RX_BUFFER; 96 | else 97 | return 0; 98 | } 99 | 100 | static u8 rtl8125_copy_from_ipc2(struct rtl8125_private *tp, u8 *dest, u32 len) 101 | { 102 | unsigned long const data_reg = r8125_get_ipc2_rx_buffer(tp); 103 | u32 offset = 0; 104 | u32 *pDword; 105 | u8 *pByte; 106 | 107 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 108 | goto exit; 109 | 110 | if (!dest) 111 | goto exit; 112 | 113 | if (len == 0) 114 | goto exit; 115 | 116 | pDword = (u32*)dest; 117 | while (len > 3 && offset < (IPC2_BUFFER_LENGTH - 4)) { 118 | *pDword++ = RTL_DASH_IPC2_R32(tp, data_reg + offset); 119 | 120 | len -= 4; 121 | offset += 4; 122 | } 123 | 124 | pByte = (u8*)pDword; 125 | while (len > 0 && offset < (IPC2_BUFFER_LENGTH - 1)) { 126 | *pByte++ = RTL_DASH_IPC2_R8(tp, data_reg + offset); 127 | 128 | len -= 1; 129 | offset += 1; 130 | } 131 | 132 | exit: 133 | return (len == 0) ? TRUE : FALSE; 134 | } 135 | 136 | static void RecvFromDashFwComplete(struct rtl8125_private *tp) 137 | { 138 | if (!tp->DASH) 139 | return; 140 | 141 | if (!HW_DASH_SUPPORT_IPC2(tp)) 142 | return; 143 | 144 | if (tp->DashReqRegValue == DASH_OOB_HDR_TYPE_REQ) { //rok 145 | RX_DASH_BUFFER_TYPE_2 rxDashBufferType2 = {0}; 146 | u32 dataLen; 147 | 148 | if (!tp->OobReq) 149 | goto exit; 150 | 151 | /* copy header for check data length */ 152 | if (!rtl8125_copy_from_ipc2(tp, 153 | (u8*)&rxDashBufferType2, 154 | sizeof(rxDashBufferType2))) 155 | goto exit; 156 | 157 | dataLen = (u16)rxDashBufferType2.oobhdr.len; 158 | 159 | tp->AfterRecvFromFwBufLen = dataLen + sizeof(OSOOBHdr); 160 | if (tp->AfterRecvFromFwBufLen > tp->SizeOfRecvFromFwBuffer) { 161 | tp->AfterRecvFromFwBufLen = tp->SizeOfRecvFromFwBuffer; 162 | tp->RecvFromFwBufErrCnt++; 163 | } 164 | 165 | /* copy data */ 166 | rtl8125_copy_from_ipc2(tp, 167 | tp->AfterRecvFromFwBuf, 168 | tp->AfterRecvFromFwBufLen); 169 | 170 | r8125_write_ipc2_tx_ack(tp); 171 | 172 | tp->OobReqComplete = TRUE; 173 | 174 | tp->RecvFromDashFwCnt++; 175 | } else if (tp->DashReqRegValue == DASH_OOB_HDR_TYPE_ACK) { //rx ack 176 | if (!tp->OobAck) 177 | goto exit; 178 | 179 | tp->OobAckComplete = TRUE; 180 | 181 | tp->RecvFromDashFwCnt++; 182 | } 183 | 184 | exit: 185 | return; 186 | } 187 | 188 | static unsigned long r8125_get_ipc2_tx_buffer(struct rtl8125_private *tp) 189 | { 190 | if (HW_DASH_SUPPORT_IPC2(tp)) 191 | return IPC2_TX_BUFFER; 192 | else 193 | return 0; 194 | } 195 | 196 | static u32 rtl8125_copy_to_ipc2(struct rtl8125_private *tp, u8 *src, u32 len) 197 | { 198 | unsigned long const data_reg = r8125_get_ipc2_tx_buffer(tp); 199 | u32 offset = 0; 200 | u32 *pDword; 201 | u8 *pByte; 202 | 203 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 204 | goto exit; 205 | 206 | if (!src) 207 | goto exit; 208 | 209 | if (len == 0) 210 | goto exit; 211 | 212 | pDword = (u32*)src; 213 | while (len > 3 && offset < (IPC2_BUFFER_LENGTH - 4)) { 214 | RTL_DASH_IPC2_W32(tp, data_reg + offset, *pDword++); 215 | 216 | len -= 4; 217 | offset += 4; 218 | } 219 | 220 | pByte = (u8*)pDword; 221 | while (len > 0 && offset < (IPC2_BUFFER_LENGTH - 1)) { 222 | RTL_DASH_IPC2_W8(tp, data_reg + offset, *pByte++); 223 | 224 | len -= 1; 225 | offset += 1; 226 | } 227 | 228 | exit: 229 | return offset; 230 | } 231 | 232 | static int SendToDashFw(struct rtl8125_private *tp, u8 *src, u16 len) 233 | { 234 | POSOOBHdr pOobHdr; 235 | int rc = -1; 236 | 237 | if (!tp->DASH) 238 | goto exit; 239 | 240 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 241 | goto exit; 242 | 243 | if (TRUE == tp->SendingToFw) 244 | goto exit; 245 | 246 | if (!src) 247 | goto exit; 248 | 249 | if (len > tp->SizeOfSendToFwBuffer) 250 | goto exit; 251 | 252 | if (len < sizeof(OSOOBHdr)) 253 | goto exit; 254 | 255 | pOobHdr = (POSOOBHdr)src; 256 | if (pOobHdr->hostReqV == DASH_OOB_HDR_TYPE_REQ) { 257 | r8125_write_ipc2_tx_ack(tp); 258 | rc = 0; 259 | goto exit; 260 | } 261 | 262 | tp->SendingToFw = TRUE; 263 | 264 | rtl8125_copy_to_ipc2(tp, src, len); 265 | 266 | r8125_write_ipc2_tx_polling(tp); 267 | 268 | tp->SendingToFw = FALSE; 269 | 270 | rc = 0; 271 | 272 | exit: 273 | if (!rc) 274 | tp->AfterSendToFwBufLen = len; 275 | else 276 | tp->AfterSendToFwBufLen = 0; 277 | 278 | return rc; 279 | } 280 | 281 | static u32 rtl8125_get_ipc2_isr(struct rtl8125_private *tp) 282 | { 283 | u32 isr = 0; 284 | 285 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 286 | goto exit; 287 | 288 | isr = RTL_DASH_IPC2_R32(tp, IPC2_RX_STATUS_REG); 289 | 290 | if (isr == ULONG_MAX) 291 | isr = 0; 292 | 293 | exit: 294 | return isr; 295 | } 296 | 297 | static void rtl8125_set_ipc2_isr(struct rtl8125_private *tp, u32 val) 298 | { 299 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 300 | return; 301 | 302 | RTL_DASH_IPC2_W32(tp, IPC2_RX_CLEAR_REG, val); 303 | } 304 | 305 | void rtl8125_clear_ipc2_isr(struct rtl8125_private *tp) 306 | { 307 | rtl8125_set_ipc2_isr(tp, rtl8125_get_ipc2_isr(tp)); 308 | } 309 | 310 | void rtl8125_set_ipc2_soc_imr_bit(struct rtl8125_private *tp, u16 mask) 311 | { 312 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 313 | return; 314 | 315 | RTL_W16(tp, RISC_IMR_8125BP, RTL_R16(tp, RISC_IMR_8125BP) | mask); 316 | } 317 | 318 | void rtl8125_clear_ipc2_soc_imr_bit(struct rtl8125_private *tp, u16 mask) 319 | { 320 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 321 | return; 322 | 323 | RTL_W16(tp, RISC_IMR_8125BP, RTL_R16(tp, RISC_IMR_8125BP) & ~mask); 324 | } 325 | 326 | bool rtl8125_check_dash_interrupt(struct rtl8125_private *tp) 327 | { 328 | bool rc = false; 329 | u32 isr; 330 | 331 | if(!tp->DASH) 332 | goto exit; 333 | 334 | if (FALSE == HW_DASH_SUPPORT_IPC2(tp)) 335 | goto exit; 336 | 337 | isr = rtl8125_get_ipc2_isr(tp); 338 | 339 | if (isr & (IPC2_RX_ROK_BIT | IPC2_RX_ACK_BIT)) { 340 | set_bit(R8125_RCV_REQ_DASH_OK, tp->dash_req_flags); 341 | if (isr & IPC2_RX_ROK_BIT) 342 | tp->DashReqRegValue = DASH_OOB_HDR_TYPE_REQ; 343 | else 344 | tp->DashReqRegValue = DASH_OOB_HDR_TYPE_ACK; 345 | } 346 | 347 | rtl8125_set_ipc2_isr(tp, isr); 348 | 349 | exit: 350 | return rc; 351 | } 352 | 353 | void rtl8125_handle_dash_interrupt(struct net_device *dev) 354 | { 355 | struct rtl8125_private *tp = netdev_priv(dev); 356 | 357 | if(!tp->DASH) 358 | return; 359 | 360 | if (test_and_clear_bit(R8125_RCV_REQ_DASH_OK, tp->dash_req_flags)) 361 | RecvFromDashFwComplete(tp); 362 | } 363 | 364 | static int DashIoctlGetRcvFromFwData(struct net_device *dev, struct rtl_dash_ioctl_struct *prtl_dash_usrdata) 365 | { 366 | struct rtl8125_private *tp = netdev_priv(dev); 367 | u32 ulInfoLen; 368 | void *InformationBuffer; 369 | u32 InformationBufferLength; 370 | void *pInfo; 371 | u8 *pByte; 372 | u16 *pWord; 373 | u8 *tmpBuf; 374 | int ret = -EFAULT; 375 | 376 | if (!tp->DASH) 377 | goto exit; 378 | 379 | if (!tp->rtk_enable_diag) 380 | goto exit; 381 | 382 | if (tp->AfterRecvFromFwBufLen == 0) 383 | goto exit; 384 | 385 | InformationBufferLength = prtl_dash_usrdata->len; 386 | InformationBuffer = prtl_dash_usrdata->data_buffer; 387 | 388 | ulInfoLen = tp->AfterRecvFromFwBufLen + 2 + 2; 389 | if (InformationBufferLength < ulInfoLen) { 390 | ret = -EFAULT; 391 | goto exit; 392 | } 393 | 394 | if (!(tmpBuf = kmalloc(ulInfoLen, GFP_ATOMIC))) { 395 | ret = -ENOMEM; 396 | goto exit; 397 | } 398 | 399 | pInfo = (void*) tp->AfterRecvFromFwBuf; 400 | pWord = (u16*) tmpBuf; 401 | *pWord++ = tp->AfterRecvFromFwBufLen; 402 | pByte = (u8*)pWord; 403 | memcpy(pByte, pInfo, tp->AfterRecvFromFwBufLen); 404 | pWord = (u16*)(pByte + tp->AfterRecvFromFwBufLen); 405 | *pWord= tp->DashReqRegValue; 406 | tp->AfterRecvFromFwBufLen = 0; 407 | if (copy_to_user(InformationBuffer, tmpBuf, ulInfoLen)) { 408 | kfree(tmpBuf); 409 | ret = -EFAULT; 410 | goto exit; 411 | } 412 | kfree(tmpBuf); 413 | ret = 0; 414 | 415 | exit: 416 | return ret; 417 | } 418 | 419 | static int DashIoctlCheckSendBufferToFwComplete(struct net_device *dev, struct rtl_dash_ioctl_struct *prtl_dash_usrdata) 420 | { 421 | struct rtl8125_private *tp = netdev_priv(dev); 422 | u32 ulInfoLen; 423 | void *InformationBuffer; 424 | u32 InformationBufferLength; 425 | u16 *pWord; 426 | u8 *tmpBuf; 427 | int ret = -EFAULT; 428 | 429 | if (!tp->DASH) 430 | goto exit; 431 | 432 | if (!tp->rtk_enable_diag) 433 | goto exit; 434 | 435 | InformationBufferLength = prtl_dash_usrdata->len; 436 | InformationBuffer = prtl_dash_usrdata->data_buffer; 437 | 438 | if (tp->SendingToFw == FALSE) 439 | ulInfoLen = tp->AfterSendToFwBufLen + sizeof(u16); 440 | else 441 | ulInfoLen = sizeof(u16); 442 | 443 | if (InformationBufferLength < ulInfoLen) { 444 | ret = -EFAULT; 445 | goto exit; 446 | } 447 | 448 | if (!(tmpBuf = kmalloc(ulInfoLen, GFP_ATOMIC))) { 449 | ret = -ENOMEM; 450 | goto exit; 451 | } 452 | 453 | pWord = (u16*) tmpBuf; 454 | if (tp->SendingToFw == FALSE) { 455 | *pWord++ = tp->AfterSendToFwBufLen; 456 | memcpy(pWord, tp->AfterSendToFwBuf, tp->AfterSendToFwBufLen); 457 | tp->AfterSendToFwBufLen = 0; 458 | } else { 459 | *pWord = 0xffff; 460 | } 461 | 462 | if (copy_to_user(InformationBuffer, tmpBuf, ulInfoLen)) 463 | ret = -EFAULT; 464 | else 465 | ret = 0; 466 | 467 | kfree(tmpBuf); 468 | 469 | exit: 470 | return ret; 471 | } 472 | 473 | static int DashIoctlCheckSendBufferToFw(struct net_device *dev, struct rtl_dash_ioctl_struct *prtl_dash_usrdata) 474 | { 475 | struct rtl8125_private *tp = netdev_priv(dev); 476 | u32 ulInfoLen; 477 | void *InformationBuffer; 478 | u32 InformationBufferLength; 479 | u16 *pWord; 480 | u16 SetDataSize; 481 | int ret = -EFAULT; 482 | 483 | if (!tp->DASH) 484 | goto exit; 485 | 486 | if (!tp->rtk_enable_diag) 487 | goto exit; 488 | 489 | InformationBufferLength = prtl_dash_usrdata->len; 490 | if (!(InformationBuffer = kmalloc(InformationBufferLength, GFP_KERNEL))) { 491 | ret = -ENOMEM; 492 | goto exit; 493 | } 494 | 495 | if (copy_from_user(InformationBuffer, prtl_dash_usrdata->data_buffer, 496 | InformationBufferLength)) { 497 | ret = -EFAULT; 498 | goto free_mem; 499 | } 500 | 501 | ulInfoLen = sizeof(u16) + sizeof(u16); 502 | 503 | if (InformationBufferLength < ulInfoLen) 504 | goto free_mem; 505 | 506 | pWord = (u16*) InformationBuffer; 507 | SetDataSize = *pWord++; 508 | 509 | if (InformationBufferLength < (SetDataSize + sizeof(u16) + sizeof(u16))) { 510 | ret = -EFAULT; 511 | goto free_mem; 512 | } 513 | 514 | ret = SendToDashFw(tp, (u8*)pWord, SetDataSize); 515 | 516 | free_mem: 517 | kfree(InformationBuffer); 518 | 519 | exit: 520 | return ret; 521 | } 522 | 523 | int rtl8125_dash_ioctl(struct net_device *dev, struct ifreq *ifr) 524 | { 525 | struct rtl8125_private *tp = netdev_priv(dev); 526 | void *user_data = ifr->ifr_data; 527 | struct rtl_dash_ioctl_struct rtl_dash_usrdata; 528 | 529 | int ret=0; 530 | 531 | if (FALSE == HW_DASH_SUPPORT_DASH(tp)) 532 | return -EOPNOTSUPP; 533 | 534 | if (!tp->DASH) 535 | return -EINVAL; 536 | 537 | if (copy_from_user(&rtl_dash_usrdata, user_data, 538 | sizeof(struct rtl_dash_ioctl_struct))) 539 | return -EFAULT; 540 | 541 | switch (rtl_dash_usrdata.cmd) { 542 | case RTL_DASH_SEND_BUFFER_DATA_TO_DASH_FW: 543 | ret = DashIoctlCheckSendBufferToFw(dev, &rtl_dash_usrdata); 544 | break; 545 | case RTL_DASH_CHECK_SEND_BUFFER_TO_DASH_FW_COMPLETE: 546 | ret = DashIoctlCheckSendBufferToFwComplete(dev, 547 | &rtl_dash_usrdata); 548 | break; 549 | case RTL_DASH_GET_RCV_FROM_FW_BUFFER_DATA: 550 | ret = DashIoctlGetRcvFromFwData(dev, &rtl_dash_usrdata); 551 | break; 552 | case RTL_DASH_OOB_REQ: 553 | tp->OobReq = TRUE; 554 | tp->OobReqComplete = FALSE; 555 | break; 556 | case RTL_DASH_OOB_ACK: 557 | tp->OobAck = TRUE; 558 | tp->OobAckComplete = FALSE; 559 | break; 560 | case RTL_DASH_DETACH_OOB_REQ: 561 | tp->OobReq = FALSE; 562 | tp->OobReqComplete = FALSE; 563 | break; 564 | case RTL_DASH_DETACH_OOB_ACK: 565 | tp->OobAck = FALSE; 566 | tp->OobAckComplete = FALSE; 567 | break; 568 | default: 569 | return -EOPNOTSUPP; 570 | } 571 | 572 | return ret; 573 | } 574 | -------------------------------------------------------------------------------- /r8125_rss.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #include 36 | #include "r8125.h" 37 | 38 | enum rtl8125_rss_register_content { 39 | /* RSS */ 40 | RSS_CTRL_TCP_IPV4_SUPP = (1 << 0), 41 | RSS_CTRL_IPV4_SUPP = (1 << 1), 42 | RSS_CTRL_TCP_IPV6_SUPP = (1 << 2), 43 | RSS_CTRL_IPV6_SUPP = (1 << 3), 44 | RSS_CTRL_IPV6_EXT_SUPP = (1 << 4), 45 | RSS_CTRL_TCP_IPV6_EXT_SUPP = (1 << 5), 46 | RSS_HALF_SUPP = (1 << 7), 47 | RSS_CTRL_UDP_IPV4_SUPP = (1 << 11), 48 | RSS_CTRL_UDP_IPV6_SUPP = (1 << 12), 49 | RSS_CTRL_UDP_IPV6_EXT_SUPP = (1 << 13), 50 | RSS_QUAD_CPU_EN = (1 << 16), 51 | RSS_HQ_Q_SUP_R = (1 << 31), 52 | }; 53 | 54 | static int rtl8125_get_rss_hash_opts(struct rtl8125_private *tp, 55 | struct ethtool_rxnfc *cmd) 56 | { 57 | cmd->data = 0; 58 | 59 | /* Report default options for RSS */ 60 | switch (cmd->flow_type) { 61 | case TCP_V4_FLOW: 62 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 63 | fallthrough; 64 | case UDP_V4_FLOW: 65 | if (tp->rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV4) 66 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 67 | fallthrough; 68 | case IPV4_FLOW: 69 | cmd->data |= RXH_IP_SRC | RXH_IP_DST; 70 | break; 71 | case TCP_V6_FLOW: 72 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 73 | fallthrough; 74 | case UDP_V6_FLOW: 75 | if (tp->rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV6) 76 | cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 77 | fallthrough; 78 | case IPV6_FLOW: 79 | cmd->data |= RXH_IP_SRC | RXH_IP_DST; 80 | break; 81 | default: 82 | return -EINVAL; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | int rtl8125_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 89 | u32 *rule_locs) 90 | { 91 | struct rtl8125_private *tp = netdev_priv(dev); 92 | int ret = -EOPNOTSUPP; 93 | 94 | if (!(dev->features & NETIF_F_RXHASH)) 95 | return ret; 96 | 97 | switch (cmd->cmd) { 98 | case ETHTOOL_GRXRINGS: 99 | cmd->data = rtl8125_tot_rx_rings(tp); 100 | ret = 0; 101 | break; 102 | case ETHTOOL_GRXFH: 103 | ret = rtl8125_get_rss_hash_opts(tp, cmd); 104 | break; 105 | default: 106 | break; 107 | } 108 | 109 | return ret; 110 | } 111 | 112 | u32 rtl8125_rss_indir_tbl_entries(struct rtl8125_private *tp) 113 | { 114 | return tp->HwSuppIndirTblEntries; 115 | } 116 | 117 | #define RSS_MASK_BITS_OFFSET (8) 118 | #define RSS_CPU_NUM_OFFSET (16) 119 | #define RTL8125_UDP_RSS_FLAGS (RTL_8125_RSS_FLAG_HASH_UDP_IPV4 | \ 120 | RTL_8125_RSS_FLAG_HASH_UDP_IPV6) 121 | static int _rtl8125_set_rss_hash_opt(struct rtl8125_private *tp) 122 | { 123 | u32 rss_flags = tp->rss_flags; 124 | u32 hash_mask_len; 125 | u32 rss_ctrl; 126 | 127 | rss_ctrl = ilog2(rtl8125_tot_rx_rings(tp)); 128 | rss_ctrl &= (BIT_0 | BIT_1 | BIT_2); 129 | rss_ctrl <<= RSS_CPU_NUM_OFFSET; 130 | 131 | /* Perform hash on these packet types */ 132 | rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP 133 | | RSS_CTRL_IPV4_SUPP 134 | | RSS_CTRL_IPV6_SUPP 135 | | RSS_CTRL_IPV6_EXT_SUPP 136 | | RSS_CTRL_TCP_IPV6_SUPP 137 | | RSS_CTRL_TCP_IPV6_EXT_SUPP; 138 | 139 | if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV4) 140 | rss_ctrl |= RSS_CTRL_UDP_IPV4_SUPP; 141 | 142 | if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV6) 143 | rss_ctrl |= RSS_CTRL_UDP_IPV6_SUPP | 144 | RSS_CTRL_UDP_IPV6_EXT_SUPP; 145 | 146 | hash_mask_len = ilog2(rtl8125_rss_indir_tbl_entries(tp)); 147 | hash_mask_len &= (BIT_0 | BIT_1 | BIT_2); 148 | rss_ctrl |= hash_mask_len << RSS_MASK_BITS_OFFSET; 149 | 150 | RTL_W32(tp, RSS_CTRL_8125, rss_ctrl); 151 | 152 | return 0; 153 | } 154 | 155 | static int rtl8125_set_rss_hash_opt(struct rtl8125_private *tp, 156 | struct ethtool_rxnfc *nfc) 157 | { 158 | u32 rss_flags = tp->rss_flags; 159 | 160 | /* 161 | * RSS does not support anything other than hashing 162 | * to queues on src and dst IPs and ports 163 | */ 164 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | 165 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) 166 | return -EINVAL; 167 | 168 | switch (nfc->flow_type) { 169 | case TCP_V4_FLOW: 170 | case TCP_V6_FLOW: 171 | if (!(nfc->data & RXH_IP_SRC) || 172 | !(nfc->data & RXH_IP_DST) || 173 | !(nfc->data & RXH_L4_B_0_1) || 174 | !(nfc->data & RXH_L4_B_2_3)) 175 | return -EINVAL; 176 | break; 177 | case UDP_V4_FLOW: 178 | if (!(nfc->data & RXH_IP_SRC) || 179 | !(nfc->data & RXH_IP_DST)) 180 | return -EINVAL; 181 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 182 | case 0: 183 | rss_flags &= ~RTL_8125_RSS_FLAG_HASH_UDP_IPV4; 184 | break; 185 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 186 | rss_flags |= RTL_8125_RSS_FLAG_HASH_UDP_IPV4; 187 | break; 188 | default: 189 | return -EINVAL; 190 | } 191 | break; 192 | case UDP_V6_FLOW: 193 | if (!(nfc->data & RXH_IP_SRC) || 194 | !(nfc->data & RXH_IP_DST)) 195 | return -EINVAL; 196 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 197 | case 0: 198 | rss_flags &= ~RTL_8125_RSS_FLAG_HASH_UDP_IPV6; 199 | break; 200 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 201 | rss_flags |= RTL_8125_RSS_FLAG_HASH_UDP_IPV6; 202 | break; 203 | default: 204 | return -EINVAL; 205 | } 206 | break; 207 | case SCTP_V4_FLOW: 208 | case AH_ESP_V4_FLOW: 209 | case AH_V4_FLOW: 210 | case ESP_V4_FLOW: 211 | case SCTP_V6_FLOW: 212 | case AH_ESP_V6_FLOW: 213 | case AH_V6_FLOW: 214 | case ESP_V6_FLOW: 215 | case IP_USER_FLOW: 216 | case ETHER_FLOW: 217 | /* RSS is not supported for these protocols */ 218 | if (nfc->data) { 219 | netif_err(tp, drv, tp->dev, "Command parameters not supported\n"); 220 | return -EINVAL; 221 | } 222 | return 0; 223 | break; 224 | default: 225 | return -EINVAL; 226 | } 227 | 228 | /* if we changed something we need to update flags */ 229 | if (rss_flags != tp->rss_flags) { 230 | u32 rss_ctrl = RTL_R32(tp, RSS_CTRL_8125); 231 | 232 | if ((rss_flags & RTL8125_UDP_RSS_FLAGS) && 233 | !(tp->rss_flags & RTL8125_UDP_RSS_FLAGS)) 234 | netdev_warn(tp->dev, 235 | "enabling UDP RSS: fragmented packets may " 236 | "arrive out of order to the stack above\n"); 237 | 238 | tp->rss_flags = rss_flags; 239 | 240 | /* Perform hash on these packet types */ 241 | rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP 242 | | RSS_CTRL_IPV4_SUPP 243 | | RSS_CTRL_IPV6_SUPP 244 | | RSS_CTRL_IPV6_EXT_SUPP 245 | | RSS_CTRL_TCP_IPV6_SUPP 246 | | RSS_CTRL_TCP_IPV6_EXT_SUPP; 247 | 248 | rss_ctrl &= ~(RSS_CTRL_UDP_IPV4_SUPP | 249 | RSS_CTRL_UDP_IPV6_SUPP | 250 | RSS_CTRL_UDP_IPV6_EXT_SUPP); 251 | 252 | if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV4) 253 | rss_ctrl |= RSS_CTRL_UDP_IPV4_SUPP; 254 | 255 | if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV6) 256 | rss_ctrl |= RSS_CTRL_UDP_IPV6_SUPP | 257 | RSS_CTRL_UDP_IPV6_EXT_SUPP; 258 | 259 | RTL_W32(tp, RSS_CTRL_8125, rss_ctrl); 260 | } 261 | 262 | return 0; 263 | } 264 | 265 | int rtl8125_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 266 | { 267 | struct rtl8125_private *tp = netdev_priv(dev); 268 | int ret = -EOPNOTSUPP; 269 | 270 | if (!(dev->features & NETIF_F_RXHASH)) 271 | return ret; 272 | 273 | switch (cmd->cmd) { 274 | case ETHTOOL_SRXFH: 275 | ret = rtl8125_set_rss_hash_opt(tp, cmd); 276 | break; 277 | default: 278 | break; 279 | } 280 | 281 | return ret; 282 | } 283 | 284 | static u32 _rtl8125_get_rxfh_key_size(struct rtl8125_private *tp) 285 | { 286 | return sizeof(tp->rss_key); 287 | } 288 | 289 | u32 rtl8125_get_rxfh_key_size(struct net_device *dev) 290 | { 291 | struct rtl8125_private *tp = netdev_priv(dev); 292 | 293 | if (!(dev->features & NETIF_F_RXHASH)) 294 | return 0; 295 | 296 | return _rtl8125_get_rxfh_key_size(tp); 297 | } 298 | 299 | u32 rtl8125_rss_indir_size(struct net_device *dev) 300 | { 301 | struct rtl8125_private *tp = netdev_priv(dev); 302 | 303 | if (!(dev->features & NETIF_F_RXHASH)) 304 | return 0; 305 | 306 | return rtl8125_rss_indir_tbl_entries(tp); 307 | } 308 | 309 | static void rtl8125_get_reta(struct rtl8125_private *tp, u32 *indir) 310 | { 311 | int i, reta_size = rtl8125_rss_indir_tbl_entries(tp); 312 | 313 | for (i = 0; i < reta_size; i++) 314 | indir[i] = tp->rss_indir_tbl[i]; 315 | } 316 | 317 | static u32 rtl8125_rss_key_reg(struct rtl8125_private *tp) 318 | { 319 | return RSS_KEY_8125; 320 | } 321 | 322 | static u32 rtl8125_rss_indir_tbl_reg(struct rtl8125_private *tp) 323 | { 324 | return RSS_INDIRECTION_TBL_8125_V2; 325 | } 326 | 327 | static void rtl8125_store_reta(struct rtl8125_private *tp) 328 | { 329 | u16 indir_tbl_reg = rtl8125_rss_indir_tbl_reg(tp); 330 | u32 i, reta_entries = rtl8125_rss_indir_tbl_entries(tp); 331 | u32 reta = 0; 332 | u8 *indir_tbl = tp->rss_indir_tbl; 333 | 334 | /* Write redirection table to HW */ 335 | for (i = 0; i < reta_entries; i++) { 336 | reta |= indir_tbl[i] << (i & 0x3) * 8; 337 | if ((i & 3) == 3) { 338 | RTL_W32(tp, indir_tbl_reg, reta); 339 | 340 | indir_tbl_reg += 4; 341 | reta = 0; 342 | } 343 | } 344 | } 345 | 346 | static void rtl8125_store_rss_key(struct rtl8125_private *tp) 347 | { 348 | const u16 rss_key_reg = rtl8125_rss_key_reg(tp); 349 | u32 i, rss_key_size = _rtl8125_get_rxfh_key_size(tp); 350 | u32 *rss_key = (u32*)tp->rss_key; 351 | 352 | /* Write redirection table to HW */ 353 | for (i = 0; i < rss_key_size; i+=4) 354 | RTL_W32(tp, rss_key_reg + i, *rss_key++); 355 | } 356 | 357 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,8,0) 358 | int rtl8125_get_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh) 359 | { 360 | struct rtl8125_private *tp = netdev_priv(dev); 361 | 362 | if (!(dev->features & NETIF_F_RXHASH)) 363 | return -EOPNOTSUPP; 364 | 365 | rxfh->hfunc = ETH_RSS_HASH_TOP; 366 | 367 | if (rxfh->indir) 368 | rtl8125_get_reta(tp, rxfh->indir); 369 | 370 | if (rxfh->key) 371 | memcpy(rxfh->key, tp->rss_key, RTL8125_RSS_KEY_SIZE); 372 | 373 | return 0; 374 | } 375 | 376 | int rtl8125_set_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh, 377 | struct netlink_ext_ack *extack) 378 | { 379 | struct rtl8125_private *tp = netdev_priv(dev); 380 | int i; 381 | u32 reta_entries = rtl8125_rss_indir_tbl_entries(tp); 382 | 383 | /* We require at least one supported parameter to be changed and no 384 | * change in any of the unsupported parameters 385 | */ 386 | if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && rxfh->hfunc != ETH_RSS_HASH_TOP) 387 | return -EOPNOTSUPP; 388 | 389 | /* Fill out the redirection table */ 390 | if (rxfh->indir) { 391 | int max_queues = tp->num_rx_rings; 392 | 393 | /* Verify user input. */ 394 | for (i = 0; i < reta_entries; i++) 395 | if (rxfh->indir[i] >= max_queues) 396 | return -EINVAL; 397 | 398 | for (i = 0; i < reta_entries; i++) 399 | tp->rss_indir_tbl[i] = rxfh->indir[i]; 400 | } 401 | 402 | /* Fill out the rss hash key */ 403 | if (rxfh->key) 404 | memcpy(tp->rss_key, rxfh->key, RTL8125_RSS_KEY_SIZE); 405 | 406 | rtl8125_store_reta(tp); 407 | 408 | rtl8125_store_rss_key(tp); 409 | 410 | return 0; 411 | } 412 | #else 413 | int rtl8125_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, 414 | u8 *hfunc) 415 | { 416 | struct rtl8125_private *tp = netdev_priv(dev); 417 | 418 | if (!(dev->features & NETIF_F_RXHASH)) 419 | return -EOPNOTSUPP; 420 | 421 | if (hfunc) 422 | *hfunc = ETH_RSS_HASH_TOP; 423 | 424 | if (indir) 425 | rtl8125_get_reta(tp, indir); 426 | 427 | if (key) 428 | memcpy(key, tp->rss_key, RTL8125_RSS_KEY_SIZE); 429 | 430 | return 0; 431 | } 432 | 433 | int rtl8125_set_rxfh(struct net_device *dev, const u32 *indir, 434 | const u8 *key, const u8 hfunc) 435 | { 436 | struct rtl8125_private *tp = netdev_priv(dev); 437 | int i; 438 | u32 reta_entries = rtl8125_rss_indir_tbl_entries(tp); 439 | 440 | /* We require at least one supported parameter to be changed and no 441 | * change in any of the unsupported parameters 442 | */ 443 | if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 444 | return -EOPNOTSUPP; 445 | 446 | /* Fill out the redirection table */ 447 | if (indir) { 448 | int max_queues = tp->num_rx_rings; 449 | 450 | /* Verify user input. */ 451 | for (i = 0; i < reta_entries; i++) 452 | if (indir[i] >= max_queues) 453 | return -EINVAL; 454 | 455 | for (i = 0; i < reta_entries; i++) 456 | tp->rss_indir_tbl[i] = indir[i]; 457 | } 458 | 459 | /* Fill out the rss hash key */ 460 | if (key) 461 | memcpy(tp->rss_key, key, RTL8125_RSS_KEY_SIZE); 462 | 463 | rtl8125_store_reta(tp); 464 | 465 | rtl8125_store_rss_key(tp); 466 | 467 | return 0; 468 | } 469 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,8,0) */ 470 | 471 | static u32 rtl8125_get_rx_desc_hash(struct rtl8125_private *tp, 472 | struct RxDesc *desc) 473 | { 474 | switch (tp->InitRxDescType) { 475 | case RX_DESC_RING_TYPE_3: 476 | return le32_to_cpu(((struct RxDescV3 *)desc)->RxDescNormalDDWord2.RSSResult); 477 | case RX_DESC_RING_TYPE_4: 478 | return le32_to_cpu(((struct RxDescV4 *)desc)->RxDescNormalDDWord1.RSSResult); 479 | default: 480 | return 0; 481 | } 482 | } 483 | 484 | #define RXS_8125B_RSS_UDP BIT(9) 485 | #define RXS_8125_RSS_IPV4 BIT(10) 486 | #define RXS_8125_RSS_IPV6 BIT(12) 487 | #define RXS_8125_RSS_TCP BIT(13) 488 | #define RTL8125_RXS_RSS_L3_TYPE_MASK (RXS_8125_RSS_IPV4 | RXS_8125_RSS_IPV6) 489 | #define RTL8125_RXS_RSS_L4_TYPE_MASK (RXS_8125_RSS_TCP | RXS_8125B_RSS_UDP) 490 | 491 | #define RXS_8125B_RSS_UDP_V4 BIT(27) 492 | #define RXS_8125_RSS_IPV4_V4 BIT(28) 493 | #define RXS_8125_RSS_IPV6_V4 BIT(29) 494 | #define RXS_8125_RSS_TCP_V4 BIT(30) 495 | #define RTL8125_RXS_RSS_L3_TYPE_MASK_V4 (RXS_8125_RSS_IPV4_V4 | RXS_8125_RSS_IPV6_V4) 496 | #define RTL8125_RXS_RSS_L4_TYPE_MASK_V4 (RXS_8125_RSS_TCP_V4 | RXS_8125B_RSS_UDP_V4) 497 | static void rtl8125_rx_hash_v3(struct rtl8125_private *tp, 498 | struct RxDescV3 *descv3, 499 | struct sk_buff *skb) 500 | { 501 | u16 rss_header_info; 502 | 503 | if (!(tp->dev->features & NETIF_F_RXHASH)) 504 | return; 505 | 506 | rss_header_info = le16_to_cpu(descv3->RxDescNormalDDWord2.HeaderInfo); 507 | 508 | if (!(rss_header_info & RTL8125_RXS_RSS_L3_TYPE_MASK)) 509 | return; 510 | 511 | skb_set_hash(skb, rtl8125_get_rx_desc_hash(tp, (struct RxDesc *)descv3), 512 | (RTL8125_RXS_RSS_L4_TYPE_MASK & rss_header_info) ? 513 | PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); 514 | } 515 | 516 | static void rtl8125_rx_hash_v4(struct rtl8125_private *tp, 517 | struct RxDescV4 *descv4, 518 | struct sk_buff *skb) 519 | { 520 | u32 rss_header_info; 521 | 522 | if (!(tp->dev->features & NETIF_F_RXHASH)) 523 | return; 524 | 525 | rss_header_info = le32_to_cpu(descv4->RxDescNormalDDWord1.RSSInfo); 526 | 527 | if (!(rss_header_info & RTL8125_RXS_RSS_L3_TYPE_MASK_V4)) 528 | return; 529 | 530 | skb_set_hash(skb, rtl8125_get_rx_desc_hash(tp, (struct RxDesc *)descv4), 531 | (RTL8125_RXS_RSS_L4_TYPE_MASK_V4 & rss_header_info) ? 532 | PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); 533 | } 534 | 535 | void rtl8125_rx_hash(struct rtl8125_private *tp, 536 | struct RxDesc *desc, 537 | struct sk_buff *skb) 538 | { 539 | switch (tp->InitRxDescType) { 540 | case RX_DESC_RING_TYPE_3: 541 | rtl8125_rx_hash_v3(tp, (struct RxDescV3 *)desc, skb); 542 | break; 543 | case RX_DESC_RING_TYPE_4: 544 | rtl8125_rx_hash_v4(tp, (struct RxDescV4 *)desc, skb); 545 | break; 546 | default: 547 | return; 548 | } 549 | } 550 | 551 | void rtl8125_disable_rss(struct rtl8125_private *tp) 552 | { 553 | RTL_W32(tp, RSS_CTRL_8125, 0x00); 554 | } 555 | 556 | void _rtl8125_config_rss(struct rtl8125_private *tp) 557 | { 558 | _rtl8125_set_rss_hash_opt(tp); 559 | 560 | rtl8125_store_reta(tp); 561 | 562 | rtl8125_store_rss_key(tp); 563 | } 564 | 565 | void rtl8125_config_rss(struct rtl8125_private *tp) 566 | { 567 | if (!tp->EnableRss) { 568 | rtl8125_disable_rss(tp); 569 | return; 570 | } 571 | 572 | _rtl8125_config_rss(tp); 573 | } 574 | 575 | void rtl8125_init_rss(struct rtl8125_private *tp) 576 | { 577 | int i; 578 | 579 | for (i = 0; i < rtl8125_rss_indir_tbl_entries(tp); i++) 580 | tp->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings); 581 | 582 | netdev_rss_key_fill(tp->rss_key, RTL8125_RSS_KEY_SIZE); 583 | } 584 | -------------------------------------------------------------------------------- /r8125_ptp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | ################################################################################ 4 | # 5 | # r8125 is the Linux device driver released for Realtek 2.5 Gigabit Ethernet 6 | # controllers with PCI-Express interface. 7 | # 8 | # Copyright(c) 2025 Realtek Semiconductor Corp. All rights reserved. 9 | # 10 | # This program is free software; you can redistribute it and/or modify it 11 | # under the terms of the GNU General Public License as published by the Free 12 | # Software Foundation; either version 2 of the License, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT 16 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | # more details. 19 | # 20 | # You should have received a copy of the GNU General Public License along with 21 | # this program; if not, see . 22 | # 23 | # Author: 24 | # Realtek NIC software team 25 | # No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan 26 | # 27 | ################################################################################ 28 | */ 29 | 30 | /************************************************************************************ 31 | * This product is covered by one or more of the following patents: 32 | * US6,570,884, US6,115,776, and US6,327,625. 33 | ***********************************************************************************/ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "r8125.h" 47 | #include "r8125_ptp.h" 48 | 49 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) 50 | static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) 51 | { 52 | return *(const struct timespec *)&ts64; 53 | } 54 | 55 | static inline struct timespec64 timespec_to_timespec64(const struct timespec ts) 56 | { 57 | return *(const struct timespec64 *)&ts; 58 | } 59 | #endif 60 | 61 | static int _rtl8125_mac_phc_gettime(struct rtl8125_private *tp, struct timespec64 *ts64) 62 | { 63 | //get local time 64 | RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_LATCHED_LOCAL_TIME | PTP_EXEC_CMD)); 65 | 66 | /* nanoseconds */ 67 | //0x6808[29:0] 68 | ts64->tv_nsec = (RTL_R32(tp, PTP_SOFT_CONFIG_Time_NS_8125) & 0x3fffffff); 69 | 70 | /* seconds */ 71 | //0x680C[47:0] 72 | ts64->tv_sec = RTL_R16(tp, PTP_SOFT_CONFIG_Time_S_8125 + 4); 73 | ts64->tv_sec <<= 32; 74 | ts64->tv_sec |= RTL_R32(tp, PTP_SOFT_CONFIG_Time_S_8125); 75 | 76 | return 0; 77 | } 78 | 79 | static void rtl8125_wait_phy_clkadj_ready(struct rtl8125_private *tp) 80 | { 81 | int i; 82 | 83 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) 84 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CLK_CFG_8126) & CLKADJ_MODE_SET)) 85 | break; 86 | } 87 | 88 | static void rtl8125_phy_set_clkadj_mode(struct rtl8125_private *tp, u16 cmd) 89 | { 90 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 91 | PTP_CLK_CFG_8126, 92 | BIT_3 | BIT_2 | BIT_1, 93 | CLKADJ_MODE_SET | cmd); 94 | 95 | rtl8125_wait_phy_clkadj_ready(tp); 96 | } 97 | 98 | static int _rtl8125_phy_phc_gettime(struct rtl8125_private *tp, struct timespec64 *ts64) 99 | { 100 | unsigned long flags; 101 | int i; 102 | 103 | r8125_spin_lock(&tp->phy_lock, flags); 104 | 105 | //Direct Read 106 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 107 | PTP_CLK_CFG_8126, 108 | BIT_3 | BIT_2 | BIT_1, 109 | (PTP_CLKADJ_MODE_SET | DIRECT_READ)); 110 | 111 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 112 | udelay(R8125_CHANNEL_WAIT_TIME); 113 | 114 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CLK_CFG_8126) & PTP_CLKADJ_MODE_SET)) 115 | break; 116 | } 117 | 118 | /* nanoseconds */ 119 | //Ns[29:16] E414[13:0] 120 | ts64->tv_nsec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_NS_HI_8126) & 0x3fff; 121 | ts64->tv_nsec <<= 16; 122 | //Ns[15:0] E412[15:0] 123 | ts64->tv_nsec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_NS_LO_8126); 124 | 125 | 126 | /* seconds */ 127 | //S[47:32] E41A[15:0] 128 | ts64->tv_sec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_HI_8126); 129 | ts64->tv_sec <<= 16; 130 | //S[31:16] E418[15:0] 131 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_MI_8126); 132 | ts64->tv_sec <<= 16; 133 | //S[15:0] E416[15:0] 134 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_LO_8126); 135 | 136 | r8125_spin_unlock(&tp->phy_lock, flags); 137 | 138 | return 0; 139 | } 140 | 141 | static int _rtl8125_mac_phc_settime(struct rtl8125_private *tp, const struct timespec64 *ts64) 142 | { 143 | /* nanoseconds */ 144 | //0x6808[29:0] 145 | RTL_W32(tp, PTP_SOFT_CONFIG_Time_NS_8125, (ts64->tv_nsec & 0x3fffffff)); 146 | 147 | /* seconds */ 148 | //0x680C[47:0] 149 | RTL_W32(tp, PTP_SOFT_CONFIG_Time_S_8125, ts64->tv_sec); 150 | RTL_W16(tp, PTP_SOFT_CONFIG_Time_S_8125 + 4, (ts64->tv_sec >> 32)); 151 | 152 | //set local time 153 | RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_SET_LOCAL_TIME | PTP_EXEC_CMD)); 154 | 155 | return 0; 156 | } 157 | 158 | static int _rtl8125_phy_phc_settime(struct rtl8125_private *tp, const struct timespec64 *ts64) 159 | { 160 | unsigned long flags; 161 | int i; 162 | 163 | r8125_spin_lock(&tp->phy_lock, flags); 164 | 165 | /* nanoseconds */ 166 | //Ns[15:0] E412[15:0] 167 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_LO_8126, ts64->tv_nsec); 168 | //Ns[29:16] E414[13:0] 169 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_HI_8126, (ts64->tv_nsec & 0x3fff0000) >> 16); 170 | 171 | /* seconds */ 172 | //S[15:0] E416[15:0] 173 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_LO_8126, ts64->tv_sec); 174 | //S[31:16] E418[15:0] 175 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_MI_8126, (ts64->tv_sec >> 16)); 176 | //S[47:32] E41A[15:0] 177 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_HI_8126, (ts64->tv_sec >> 32)); 178 | 179 | //Direct Write 180 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 181 | PTP_CLK_CFG_8126, 182 | BIT_3 | BIT_2 | BIT_1, 183 | (PTP_CLKADJ_MODE_SET | DIRECT_WRITE)); 184 | 185 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 186 | udelay(R8125_CHANNEL_WAIT_TIME); 187 | 188 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CLK_CFG_8126) & PTP_CLKADJ_MODE_SET)) 189 | break; 190 | } 191 | 192 | r8125_spin_unlock(&tp->phy_lock, flags); 193 | 194 | return 0; 195 | } 196 | 197 | static int _rtl8125_mac_phc_adjtime(struct rtl8125_private *tp, s64 delta) 198 | { 199 | struct timespec64 d; 200 | bool negative = false; 201 | u64 tohw; 202 | u32 nsec; 203 | u64 sec; 204 | 205 | if (delta < 0) { 206 | negative = true; 207 | tohw = -delta; 208 | } else { 209 | tohw = delta; 210 | } 211 | 212 | d = ns_to_timespec64(tohw); 213 | 214 | nsec = d.tv_nsec; 215 | sec = d.tv_sec; 216 | 217 | if (negative) { 218 | nsec = -nsec; 219 | sec = -sec; 220 | } 221 | 222 | nsec &= 0x3fffffff; 223 | sec &= 0x0000ffffffffffff; 224 | 225 | if (negative) { 226 | nsec |= PTP_SOFT_CONFIG_TIME_NS_NEGATIVE; 227 | sec |= PTP_SOFT_CONFIG_TIME_S_NEGATIVE; 228 | } 229 | 230 | /* nanoseconds */ 231 | //0x6808[29:0] 232 | RTL_W32(tp, PTP_SOFT_CONFIG_Time_NS_8125, nsec); 233 | 234 | /* seconds */ 235 | //0x680C[47:0] 236 | RTL_W32(tp, PTP_SOFT_CONFIG_Time_S_8125, sec); 237 | RTL_W16(tp, PTP_SOFT_CONFIG_Time_S_8125 + 4, (sec >> 32)); 238 | 239 | //adjust local time 240 | //RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_DRIFT_LOCAL_TIME | PTP_EXEC_CMD)); 241 | RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_SET_LOCAL_TIME | PTP_EXEC_CMD)); 242 | 243 | return 0; 244 | } 245 | 246 | static int rtl8125_mac_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) 247 | { 248 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 249 | int ret; 250 | 251 | //netif_info(tp, drv, tp->dev, "phc adjust time\n"); 252 | 253 | rtnl_lock(); 254 | ret = _rtl8125_mac_phc_adjtime(tp, delta); 255 | rtnl_unlock(); 256 | 257 | return ret; 258 | } 259 | 260 | static int _rtl8125_phy_phc_adjtime(struct rtl8125_private *tp, s64 delta) 261 | { 262 | unsigned long flags; 263 | struct timespec64 d; 264 | bool negative = false; 265 | int i; 266 | u64 tohw; 267 | u32 nsec; 268 | u64 sec; 269 | 270 | if (delta < 0) { 271 | negative = true; 272 | tohw = -delta; 273 | } else { 274 | tohw = delta; 275 | } 276 | 277 | d = ns_to_timespec64(tohw); 278 | 279 | nsec = d.tv_nsec; 280 | sec = d.tv_sec; 281 | 282 | nsec &= 0x3fffffff; 283 | sec &= 0x0000ffffffffffff; 284 | 285 | r8125_spin_lock(&tp->phy_lock, flags); 286 | 287 | /* nanoseconds */ 288 | //Ns[15:0] E412[15:0] 289 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_LO_8126, nsec); 290 | //Ns[29:16] E414[13:0] 291 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_HI_8126, (nsec >> 16)); 292 | 293 | /* seconds */ 294 | //S[15:0] E416[15:0] 295 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_LO_8126, sec); 296 | //S[31:16] E418[15:0] 297 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_MI_8126, (sec >> 16)); 298 | //S[47:32] E41A[15:0] 299 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_S_HI_8126, (sec >> 32)); 300 | 301 | if (negative) 302 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 303 | PTP_CLK_CFG_8126, 304 | BIT_3 | BIT_2 | BIT_1, 305 | (PTP_CLKADJ_MODE_SET | DECREMENT_STEP)); 306 | else 307 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 308 | PTP_CLK_CFG_8126, 309 | BIT_3 | BIT_2 | BIT_1, 310 | (PTP_CLKADJ_MODE_SET | INCREMENT_STEP)); 311 | 312 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 313 | udelay(R8125_CHANNEL_WAIT_TIME); 314 | 315 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CLK_CFG_8126) & PTP_CLKADJ_MODE_SET)) 316 | break; 317 | } 318 | 319 | r8125_spin_unlock(&tp->phy_lock, flags); 320 | 321 | return 0; 322 | } 323 | 324 | static int rtl8125_phy_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) 325 | { 326 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 327 | int ret; 328 | 329 | //netif_info(tp, drv, tp->dev, "phc adjust time\n"); 330 | 331 | ret = _rtl8125_phy_phc_adjtime(tp, delta); 332 | 333 | return ret; 334 | } 335 | 336 | /* 337 | * 1ppm means every 125MHz plus 125Hz. It also means every 8ns minus 8ns*10^(-6) 338 | * 1ns=2^30 sub_ns 339 | * 8ns*10^(-6) = 8 * 2^30 sub_ns * 10^(-6) = 2^33 sub_ns * 10^(-6) = 8590 = 0x218E sub_ns 340 | * 341 | * 1ppb means every 125MHz plus 0.125Hz. It also means every 8ns minus 8ns*10^(-9) 342 | * 1ns=2^30 sub_ns 343 | * 8ns*10^(-9) = 8 * 2^30 sub_ns * 10^(-9) = 2^33 sub_ns * 10^(-9) = 8.59 sub_ns = 9 sub_ns 344 | */ 345 | static int _rtl8125_mac_phc_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 346 | { 347 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 348 | bool negative = false; 349 | u32 sub_ns; 350 | 351 | if (ppb < 0) { 352 | negative = true; 353 | ppb = -ppb; 354 | } 355 | 356 | sub_ns = ppb * 9; 357 | if (negative) { 358 | sub_ns = -sub_ns; 359 | sub_ns &= 0x3fffffff; 360 | sub_ns |= PTP_ADJUST_TIME_NS_NEGATIVE; 361 | } else 362 | sub_ns &= 0x3fffffff; 363 | 364 | /* nanoseconds */ 365 | //0x6808[29:0] 366 | RTL_W32(tp, PTP_SOFT_CONFIG_Time_NS_8125, sub_ns); 367 | 368 | //adjust local time 369 | RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_DRIFT_LOCAL_TIME | PTP_EXEC_CMD)); 370 | //RTL_W16(tp, PTP_TIME_CORRECT_CMD_8125, (PTP_CMD_SET_LOCAL_TIME | PTP_EXEC_CMD)); 371 | 372 | return 0; 373 | } 374 | 375 | /* 376 | * delta = delta * 10^6 ppm = delta * 10^9 ppb (in this equation ppm and ppb are not variable) 377 | * 378 | * in adjfreq ppb is a variable 379 | * ppb = delta * 10^9 380 | * delta = ppb / 10^9 381 | * rate_value = |delta| * 2^32 = |ppb| / 10^9 * 2^32 = (|ppb| << 32) / 10^9 382 | */ 383 | static int _rtl8125_phy_phc_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 384 | { 385 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 386 | unsigned long flags; 387 | u32 rate_value; 388 | 389 | if (ppb < 0) { 390 | rate_value = ((u64)-ppb << 32) / 1000000000; 391 | rate_value = ~rate_value + 1; 392 | } else 393 | rate_value = ((u64)ppb << 32) / 1000000000; 394 | 395 | r8125_spin_lock(&tp->phy_lock, flags); 396 | 397 | /* nanoseconds */ 398 | //Ns[15:0] E412[15:0] 399 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_LO_8126, rate_value); 400 | //Ns[22:16] E414[13:0] 401 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CFG_NS_HI_8126, (rate_value & 0x003f0000) >> 16); 402 | 403 | rtl8125_phy_set_clkadj_mode(tp, RATE_WRITE); 404 | 405 | r8125_spin_unlock(&tp->phy_lock, flags); 406 | 407 | return 0; 408 | } 409 | 410 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) 411 | static int rtl8125_mac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 412 | { 413 | s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 414 | 415 | if (ppb > ptp->max_adj || ppb < -ptp->max_adj) 416 | return -EINVAL; 417 | 418 | rtnl_lock(); 419 | _rtl8125_mac_phc_adjfreq(ptp, ppb); 420 | rtnl_unlock(); 421 | 422 | return 0; 423 | } 424 | #else 425 | static int rtl8125_mac_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) 426 | { 427 | //struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 428 | 429 | //netif_info(tp, drv, tp->dev, "phc adjust freq\n"); 430 | 431 | if (delta > ptp->max_adj || delta < -ptp->max_adj) 432 | return -EINVAL; 433 | 434 | rtnl_lock(); 435 | _rtl8125_mac_phc_adjfreq(ptp, delta); 436 | rtnl_unlock(); 437 | 438 | return 0; 439 | } 440 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) */ 441 | 442 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) 443 | static int rtl8125_mac_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts64, 444 | struct ptp_system_timestamp *sts) 445 | { 446 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 447 | int ret; 448 | 449 | //netif_info(tp, drv, tp->dev, "phc get ts\n"); 450 | 451 | rtnl_lock(); 452 | ptp_read_system_prets(sts); 453 | ret = _rtl8125_mac_phc_gettime(tp, ts64); 454 | ptp_read_system_postts(sts); 455 | rtnl_unlock(); 456 | 457 | return ret; 458 | } 459 | #else 460 | static int rtl8125_mac_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts64) 461 | { 462 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 463 | int ret; 464 | 465 | //netif_info(tp, drv, tp->dev, "phc get ts\n"); 466 | 467 | rtnl_lock(); 468 | ret = _rtl8125_mac_phc_gettime(tp, ts64); 469 | rtnl_unlock(); 470 | 471 | return ret; 472 | } 473 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) */ 474 | 475 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) 476 | static int rtl8125_phy_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 477 | { 478 | s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 479 | 480 | if (ppb > ptp->max_adj || ppb < -ptp->max_adj) 481 | return -EINVAL; 482 | 483 | _rtl8125_phy_phc_adjfreq(ptp, ppb); 484 | 485 | return 0; 486 | } 487 | 488 | #else 489 | static int rtl8125_phy_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) 490 | { 491 | //netif_info(tp, drv, tp->dev, "phc adjust freq\n"); 492 | 493 | if (delta > ptp->max_adj || delta < -ptp->max_adj) 494 | return -EINVAL; 495 | 496 | _rtl8125_phy_phc_adjfreq(ptp, delta); 497 | 498 | return 0; 499 | } 500 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) */ 501 | 502 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) 503 | static int rtl8125_phy_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts64, 504 | struct ptp_system_timestamp *sts) 505 | { 506 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 507 | int ret; 508 | 509 | //netif_info(tp, drv, tp->dev, "phc get ts\n"); 510 | 511 | ptp_read_system_prets(sts); 512 | ret = _rtl8125_phy_phc_gettime(tp, ts64); 513 | ptp_read_system_postts(sts); 514 | 515 | return ret; 516 | } 517 | #else 518 | static int rtl8125_phy_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts64) 519 | { 520 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 521 | int ret; 522 | 523 | //netif_info(tp, drv, tp->dev, "phc get ts\n"); 524 | 525 | ret = _rtl8125_phy_phc_gettime(tp, ts64); 526 | 527 | return ret; 528 | } 529 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) */ 530 | 531 | static int rtl8125_mac_phc_settime(struct ptp_clock_info *ptp, 532 | const struct timespec64 *ts64) 533 | { 534 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 535 | int ret; 536 | 537 | //netif_info(tp, drv, tp->dev, "phc set ts\n"); 538 | 539 | rtnl_lock(); 540 | ret = _rtl8125_mac_phc_settime(tp, ts64); 541 | rtnl_unlock(); 542 | 543 | return ret; 544 | } 545 | 546 | static int rtl8125_phy_phc_settime(struct ptp_clock_info *ptp, 547 | const struct timespec64 *ts64) 548 | { 549 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 550 | int ret; 551 | 552 | //netif_info(tp, drv, tp->dev, "phc set ts\n"); 553 | 554 | ret = _rtl8125_phy_phc_settime(tp, ts64); 555 | 556 | return ret; 557 | } 558 | 559 | static int rtl8125_mac_phc_enable(struct ptp_clock_info *ptp, 560 | struct ptp_clock_request *rq, int on) 561 | { 562 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 563 | u16 ptp_ctrl; 564 | 565 | //netif_info(tp, drv, tp->dev, "phc enable type %x on %d\n", rq->type, on); 566 | 567 | switch (rq->type) { 568 | case PTP_CLK_REQ_PPS: 569 | rtnl_lock(); 570 | ptp_ctrl = RTL_R16(tp, PTP_CTRL_8125); 571 | ptp_ctrl &= ~BIT_15; 572 | if (on) 573 | ptp_ctrl |= BIT_14; 574 | else 575 | ptp_ctrl &= ~BIT_14; 576 | RTL_W16(tp, PTP_CTRL_8125, ptp_ctrl); 577 | rtnl_unlock(); 578 | return 0; 579 | default: 580 | return -EOPNOTSUPP; 581 | } 582 | } 583 | 584 | static int rtl8125_phy_phc_enable(struct ptp_clock_info *ptp, 585 | struct ptp_clock_request *rq, int on) 586 | { 587 | struct rtl8125_private *tp = container_of(ptp, struct rtl8125_private, ptp_clock_info); 588 | unsigned long flags; 589 | u16 phy_ocp_data; 590 | 591 | switch (rq->type) { 592 | case PTP_CLK_REQ_PPS: 593 | rtnl_lock(); 594 | if (on) { 595 | tp->pps_enable = 1; 596 | rtl8125_mac_ocp_write(tp, 0xDC00, rtl8125_mac_ocp_read(tp, 0xDC00) & ~BIT_6); 597 | rtl8125_mac_ocp_write(tp, 0xDC60, rtl8125_mac_ocp_read(tp, 0xDC60) | BIT_6); 598 | 599 | r8125_spin_lock(&tp->phy_lock, flags); 600 | 601 | /* Set periodic pulse 1pps */ 602 | /* E432[8:0] = 0x017d */ 603 | phy_ocp_data = rtl8125_mdio_direct_read_phy_ocp(tp, 0xE432); 604 | phy_ocp_data &= 0xFE00; 605 | phy_ocp_data |= 0x017d; 606 | rtl8125_mdio_direct_write_phy_ocp(tp, 0xE432, phy_ocp_data); 607 | 608 | rtl8125_mdio_direct_write_phy_ocp(tp, 0xE434, 0x7840); 609 | 610 | /* E436[8:0] = 0xbe */ 611 | phy_ocp_data = rtl8125_mdio_direct_read_phy_ocp(tp, 0xE436); 612 | phy_ocp_data &= 0xFE00; 613 | phy_ocp_data |= 0xbe; 614 | rtl8125_mdio_direct_write_phy_ocp(tp, 0xE436, phy_ocp_data); 615 | 616 | rtl8125_mdio_direct_write_phy_ocp(tp, 0xE438, 0xbc20); 617 | 618 | r8125_spin_unlock(&tp->phy_lock, flags); 619 | 620 | /* start hrtimer */ 621 | hrtimer_start(&tp->pps_timer, 1000000000, HRTIMER_MODE_REL); 622 | } else 623 | tp->pps_enable = 0; 624 | rtnl_unlock(); 625 | return 0; 626 | default: 627 | return -EOPNOTSUPP; 628 | } 629 | } 630 | 631 | static void rtl8125_phy_ptp_enable_config(struct rtl8125_private *tp) 632 | { 633 | u16 ptp_ctrl; 634 | 635 | if (tp->syncE_en) 636 | rtl8125_set_eth_phy_ocp_bit(tp, PTP_SYNCE_CTL, BIT_0); 637 | else 638 | rtl8125_clear_eth_phy_ocp_bit(tp, PTP_SYNCE_CTL, BIT_0); 639 | 640 | ptp_ctrl = BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_12; 641 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_CTL, ptp_ctrl); 642 | 643 | rtl8125_set_eth_phy_ocp_bit(tp, 0xA640, BIT_15); 644 | } 645 | 646 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0) 647 | int rtl8125_get_ts_info(struct net_device *netdev, 648 | struct ethtool_ts_info *info) 649 | #else 650 | int rtl8125_get_ts_info(struct net_device *netdev, 651 | struct kernel_ethtool_ts_info *info) 652 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0) */ 653 | { 654 | struct rtl8125_private *tp = netdev_priv(netdev); 655 | 656 | /* we always support timestamping disabled */ 657 | info->rx_filters = BIT(HWTSTAMP_FILTER_NONE); 658 | 659 | if (tp->HwSuppPtpVer == 0) 660 | return ethtool_op_get_ts_info(netdev, info); 661 | 662 | info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 663 | SOF_TIMESTAMPING_RX_SOFTWARE | 664 | SOF_TIMESTAMPING_SOFTWARE | 665 | SOF_TIMESTAMPING_TX_HARDWARE | 666 | SOF_TIMESTAMPING_RX_HARDWARE | 667 | SOF_TIMESTAMPING_RAW_HARDWARE; 668 | 669 | if (tp->ptp_clock) 670 | info->phc_index = ptp_clock_index(tp->ptp_clock); 671 | else 672 | info->phc_index = -1; 673 | 674 | info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); 675 | 676 | info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 677 | BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | 678 | BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 679 | BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) | 680 | BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | 681 | BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | 682 | BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ); 683 | 684 | return 0; 685 | } 686 | 687 | static const struct ptp_clock_info rtl8125_mac_ptp_clock_info = { 688 | .owner = THIS_MODULE, 689 | .n_alarm = 0, 690 | .n_ext_ts = 0, 691 | .n_per_out = 0, 692 | .n_pins = 0, 693 | .pps = 1, 694 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) 695 | .adjfine = rtl8125_mac_ptp_adjfine, 696 | #else 697 | .adjfreq = rtl8125_mac_phc_adjfreq, 698 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) */ 699 | .adjtime = rtl8125_mac_phc_adjtime, 700 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) 701 | .gettimex64 = rtl8125_mac_phc_gettime, 702 | #else 703 | .gettime64 = rtl8125_mac_phc_gettime, 704 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) */ 705 | .settime64 = rtl8125_mac_phc_settime, 706 | .enable = rtl8125_mac_phc_enable, 707 | }; 708 | 709 | static const struct ptp_clock_info rtl8125_phy_ptp_clock_info = { 710 | .owner = THIS_MODULE, 711 | .n_alarm = 0, 712 | .n_ext_ts = 0, 713 | .n_per_out = 0, 714 | .n_pins = 0, 715 | .pps = 1, 716 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) 717 | .adjfine = rtl8125_phy_ptp_adjfine, 718 | #else 719 | .adjfreq = rtl8125_phy_phc_adjfreq, 720 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) */ 721 | .adjtime = rtl8125_phy_phc_adjtime, 722 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) 723 | .gettimex64 = rtl8125_phy_phc_gettime, 724 | #else 725 | .gettime64 = rtl8125_phy_phc_gettime, 726 | #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) */ 727 | 728 | .settime64 = rtl8125_phy_phc_settime, 729 | .enable = rtl8125_phy_phc_enable, 730 | }; 731 | 732 | static void rtl8125_mac_ptp_egresstime(struct rtl8125_private *tp, struct timespec64 *ts64, u32 regnum) 733 | { 734 | /* nanoseconds */ 735 | //[29:0] 736 | ts64->tv_nsec = rtl8125_mac_ocp_read(tp, PTP_EGRESS_TIME_BASE_NS_8125 + regnum * 16 + 2); 737 | ts64->tv_nsec <<= 16; 738 | ts64->tv_nsec |= rtl8125_mac_ocp_read(tp, PTP_EGRESS_TIME_BASE_NS_8125 + regnum * 16); 739 | ts64->tv_nsec &= 0x3fffffff; 740 | 741 | /* seconds */ 742 | //[47:0] 743 | ts64->tv_sec = rtl8125_mac_ocp_read(tp, PTP_EGRESS_TIME_BASE_S_8125 + regnum * 16 + 4); 744 | ts64->tv_sec <<= 16; 745 | ts64->tv_sec |= rtl8125_mac_ocp_read(tp, PTP_EGRESS_TIME_BASE_S_8125 + regnum * 16 + 2); 746 | ts64->tv_sec <<= 16; 747 | ts64->tv_sec |= rtl8125_mac_ocp_read(tp, PTP_EGRESS_TIME_BASE_S_8125 + regnum * 16); 748 | ts64->tv_sec &= 0x0000ffffffffffff; 749 | } 750 | 751 | static u16 rtl8125_phy_ptp_get_tx_msgtype(struct rtl8125_private *tp) 752 | { 753 | u16 tx_ts_ready = 0; 754 | int i; 755 | 756 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 757 | tx_ts_ready = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_STA) & 0xF000; 758 | if (tx_ts_ready) 759 | break; 760 | } 761 | 762 | switch (tx_ts_ready) { 763 | case TX_TS_PDLYRSP_RDY: 764 | return PTP_MSGTYPE_PDELAY_RESP; 765 | case TX_TS_PDLYREQ_RDY: 766 | return PTP_MSGTYPE_PDELAY_REQ; 767 | case TX_TS_DLYREQ_RDY: 768 | return PTP_MSGTYPE_DELAY_REQ; 769 | case TX_TS_SYNC_RDY: 770 | default: 771 | return PTP_MSGTYPE_SYNC; 772 | } 773 | } 774 | 775 | /* 776 | static u16 rtl8125_phy_ptp_get_rx_msgtype(struct rtl8125_private *tp) 777 | { 778 | u16 rx_ts_ready = 0; 779 | int i; 780 | 781 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 782 | rx_ts_ready = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_STA) & 0x0F00; 783 | if (rx_ts_ready) 784 | break; 785 | } 786 | 787 | switch (rx_ts_ready) { 788 | case RX_TS_PDLYRSP_RDY: 789 | return PTP_MSGTYPE_PDELAY_RESP; 790 | case RX_TS_PDLYREQ_RDY: 791 | return PTP_MSGTYPE_PDELAY_REQ; 792 | case RX_TS_DLYREQ_RDY: 793 | return PTP_MSGTYPE_DELAY_REQ; 794 | case RX_TS_SYNC_RDY: 795 | default: 796 | return PTP_MSGTYPE_SYNC; 797 | } 798 | } 799 | */ 800 | 801 | static void rtl8125_wait_phy_trx_ts_ready(struct rtl8125_private *tp) 802 | { 803 | int i; 804 | 805 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) 806 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_STA) & TRX_TS_RD)) 807 | break; 808 | } 809 | 810 | static void rtl8125_set_phy_trx_ts_cmd(struct rtl8125_private *tp, u16 cmd) 811 | { 812 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 813 | PTP_TRX_TS_STA, 814 | TRXTS_SEL | BIT_3 | BIT_2, 815 | TRX_TS_RD | cmd); 816 | 817 | rtl8125_wait_phy_trx_ts_ready(tp); 818 | } 819 | 820 | static void rtl8125_phy_ptp_egresstime(struct rtl8125_private *tp, struct timespec64 *ts64) 821 | { 822 | u16 msgtype; 823 | 824 | msgtype = rtl8125_phy_ptp_get_tx_msgtype(tp); 825 | 826 | msgtype <<= 2; 827 | 828 | rtl8125_set_phy_trx_ts_cmd(tp, (msgtype | BIT_4)); 829 | 830 | /* nanoseconds */ 831 | //Ns[29:16] E448[13:0] 832 | ts64->tv_nsec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_NS_HI) & 0x3fff; 833 | ts64->tv_nsec <<= 16; 834 | //Ns[15:0] E446[15:0] 835 | ts64->tv_nsec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_NS_LO); 836 | 837 | /* seconds */ 838 | //S[47:32] E44E[15:0] 839 | ts64->tv_sec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_HI); 840 | ts64->tv_sec <<= 16; 841 | //S[31:16] E44C[15:0] 842 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_MI); 843 | ts64->tv_sec <<= 16; 844 | //S[15:0] E44A[15:0] 845 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_LO); 846 | } 847 | static void rtl8125_phy_ptp_ingresstime(struct rtl8125_private *tp, struct timespec64 *ts64, u8 type) 848 | { 849 | u16 msgtype; 850 | 851 | switch (type) { 852 | case PTP_MSGTYPE_PDELAY_RESP: 853 | case PTP_MSGTYPE_PDELAY_REQ: 854 | case PTP_MSGTYPE_DELAY_REQ: 855 | case PTP_MSGTYPE_SYNC: 856 | msgtype = type << 2; 857 | break; 858 | default: 859 | return; 860 | } 861 | 862 | rtl8125_set_phy_trx_ts_cmd(tp, (TRXTS_SEL | msgtype | BIT_4)); 863 | 864 | /* nanoseconds */ 865 | //Ns[29:16] E448[13:0] 866 | ts64->tv_nsec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_NS_HI) & 0x3fff; 867 | ts64->tv_nsec <<= 16; 868 | //Ns[15:0] E446[15:0] 869 | ts64->tv_nsec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_NS_LO); 870 | 871 | /* seconds */ 872 | //S[47:32] E44E[15:0] 873 | ts64->tv_sec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_HI); 874 | ts64->tv_sec <<= 16; 875 | //S[31:16] E44C[15:0] 876 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_MI); 877 | ts64->tv_sec <<= 16; 878 | //S[15:0] E44A[15:0] 879 | ts64->tv_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_TRX_TS_S_LO); 880 | } 881 | 882 | static void rtl8125_mac_ptp_tx_hwtstamp(struct rtl8125_private *tp) 883 | { 884 | struct sk_buff *skb = tp->ptp_tx_skb; 885 | struct skb_shared_hwtstamps shhwtstamps = {0}; 886 | struct timespec64 ts64; 887 | u32 regnum; 888 | 889 | RTL_W8(tp, PTP_ISR_8125, PTP_ISR_TOK | PTP_ISR_TER); 890 | 891 | //IO 0x2302 bit 10~11 WR_PTR 892 | regnum = RTL_R16(tp, 0x2032) & 0x0C00; 893 | regnum >>= 10; 894 | regnum = (regnum + 3) % 4; 895 | 896 | rtnl_lock(); 897 | rtl8125_mac_ptp_egresstime(tp, &ts64, regnum); 898 | rtnl_unlock(); 899 | 900 | /* Upper 32 bits contain s, lower 32 bits contain ns. */ 901 | shhwtstamps.hwtstamp = ktime_set(ts64.tv_sec, 902 | ts64.tv_nsec); 903 | 904 | /* Clear the lock early before calling skb_tstamp_tx so that 905 | * applications are not woken up before the lock bit is clear. We use 906 | * a copy of the skb pointer to ensure other threads can't change it 907 | * while we're notifying the stack. 908 | */ 909 | tp->ptp_tx_skb = NULL; 910 | clear_bit_unlock(__RTL8125_PTP_TX_IN_PROGRESS, &tp->state); 911 | 912 | /* Notify the stack and free the skb after we've unlocked */ 913 | skb_tstamp_tx(skb, &shhwtstamps); 914 | dev_kfree_skb_any(skb); 915 | } 916 | 917 | static void rtl8125_phy_ptp_tx_hwtstamp(struct rtl8125_private *tp) 918 | { 919 | struct sk_buff *skb = tp->ptp_tx_skb; 920 | struct skb_shared_hwtstamps shhwtstamps = { 0 }; 921 | struct timespec64 ts64; 922 | 923 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_INSR, TX_TX_INTR); 924 | 925 | rtl8125_phy_ptp_egresstime(tp, &ts64); 926 | 927 | /* Upper 32 bits contain s, lower 32 bits contain ns. */ 928 | shhwtstamps.hwtstamp = ktime_set(ts64.tv_sec, 929 | ts64.tv_nsec); 930 | 931 | /* Clear the lock early before calling skb_tstamp_tx so that 932 | * applications are not woken up before the lock bit is clear. We use 933 | * a copy of the skb pointer to ensure other threads can't change it 934 | * while we're notifying the stack. 935 | */ 936 | tp->ptp_tx_skb = NULL; 937 | clear_bit_unlock(__RTL8125_PTP_TX_IN_PROGRESS, &tp->state); 938 | 939 | /* Notify the stack and free the skb after we've unlocked */ 940 | skb_tstamp_tx(skb, &shhwtstamps); 941 | dev_kfree_skb_any(skb); 942 | } 943 | 944 | #define RTL8125_PTP_TX_TIMEOUT (HZ * 15) 945 | static void rtl8125_mac_ptp_tx_work(struct work_struct *work) 946 | { 947 | struct rtl8125_private *tp = container_of(work, struct rtl8125_private, 948 | ptp_tx_work); 949 | 950 | if (!tp->ptp_tx_skb) 951 | return; 952 | 953 | if (time_is_before_jiffies(tp->ptp_tx_start + 954 | RTL8125_PTP_TX_TIMEOUT)) { 955 | dev_kfree_skb_any(tp->ptp_tx_skb); 956 | tp->ptp_tx_skb = NULL; 957 | clear_bit_unlock(__RTL8125_PTP_TX_IN_PROGRESS, &tp->state); 958 | tp->tx_hwtstamp_timeouts++; 959 | /* Clear the tx valid bit in TSYNCTXCTL register to enable 960 | * interrupt 961 | */ 962 | RTL_W8(tp, PTP_ISR_8125, PTP_ISR_TOK | PTP_ISR_TER); 963 | return; 964 | } 965 | 966 | if (RTL_R8(tp, PTP_ISR_8125) & (PTP_ISR_TOK)) 967 | rtl8125_mac_ptp_tx_hwtstamp(tp); 968 | else 969 | /* reschedule to check later */ 970 | schedule_work(&tp->ptp_tx_work); 971 | } 972 | 973 | static void rtl8125_phy_ptp_tx_work(struct work_struct *work) 974 | { 975 | struct rtl8125_private *tp = container_of(work, struct rtl8125_private, 976 | ptp_tx_work); 977 | unsigned long flags; 978 | bool tx_intr; 979 | 980 | if (!tp->ptp_tx_skb) 981 | return; 982 | 983 | if (time_is_before_jiffies(tp->ptp_tx_start + 984 | RTL8125_PTP_TX_TIMEOUT)) { 985 | dev_kfree_skb_any(tp->ptp_tx_skb); 986 | tp->ptp_tx_skb = NULL; 987 | clear_bit_unlock(__RTL8125_PTP_TX_IN_PROGRESS, &tp->state); 988 | tp->tx_hwtstamp_timeouts++; 989 | /* Clear the tx valid bit in TSYNCTXCTL register to enable 990 | * interrupt 991 | */ 992 | r8125_spin_lock(&tp->phy_lock, flags); 993 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_INSR, TX_TX_INTR); 994 | r8125_spin_unlock(&tp->phy_lock, flags); 995 | return; 996 | } 997 | 998 | r8125_spin_lock(&tp->phy_lock, flags); 999 | if (rtl8125_mdio_direct_read_phy_ocp(tp, PTP_INSR) & TX_TX_INTR) { 1000 | tx_intr = true; 1001 | rtl8125_phy_ptp_tx_hwtstamp(tp); 1002 | } else { 1003 | tx_intr = false; 1004 | } 1005 | r8125_spin_unlock(&tp->phy_lock, flags); 1006 | 1007 | if (!tx_intr) { 1008 | /* reschedule to check later */ 1009 | schedule_work(&tp->ptp_tx_work); 1010 | } 1011 | } 1012 | 1013 | static int rtl8125_mac_hwtstamp_enable(struct rtl8125_private *tp, bool enable) 1014 | { 1015 | RTL_W16(tp, PTP_CTRL_8125, 0); 1016 | if (enable) { 1017 | u16 ptp_ctrl; 1018 | struct timespec64 ts64; 1019 | 1020 | //clear ptp isr 1021 | RTL_W8(tp, PTP_ISR_8125, 0xff); 1022 | //ptp source 0:gphy 1:mac 1023 | rtl8125_mac_ocp_write(tp, 0xDC00, rtl8125_mac_ocp_read(tp, 0xDC00) | BIT_6); 1024 | //enable ptp 1025 | ptp_ctrl = (BIT_0 | BIT_3 | BIT_4 | BIT_6 | BIT_10 | BIT_12); 1026 | if (tp->ptp_master_mode) 1027 | ptp_ctrl |= BIT_1; 1028 | RTL_W16(tp, PTP_CTRL_8125, ptp_ctrl); 1029 | 1030 | //set system time 1031 | /* 1032 | if (ktime_to_timespec64_cond(ktime_get_real(), &ts64)) 1033 | _rtl8125_mac_phc_settime(tp, timespec64_to_timespec(ts64)); 1034 | */ 1035 | ktime_get_real_ts64(&ts64); 1036 | _rtl8125_mac_phc_settime(tp, &ts64); 1037 | } 1038 | 1039 | return 0; 1040 | } 1041 | 1042 | static int rtl8125_phy_hwtstamp_enable(struct rtl8125_private *tp, bool enable) 1043 | { 1044 | unsigned long flags; 1045 | 1046 | r8125_spin_lock(&tp->phy_lock, flags); 1047 | 1048 | if (enable) { 1049 | //trx timestamp interrupt enable 1050 | rtl8125_set_eth_phy_ocp_bit(tp, PTP_INER, BIT_2 | BIT_3); 1051 | 1052 | //set isr clear mode 1053 | rtl8125_set_eth_phy_ocp_bit(tp, PTP_GEN_CFG, BIT_0); 1054 | 1055 | //clear ptp isr 1056 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_INSR, 0xFFFF); 1057 | 1058 | //enable ptp 1059 | rtl8125_phy_ptp_enable_config(tp); 1060 | 1061 | //rtl8125_set_phy_local_time(tp); 1062 | } else { 1063 | /* trx timestamp interrupt disable */ 1064 | rtl8125_clear_eth_phy_ocp_bit(tp, PTP_INER, BIT_2 | BIT_3); 1065 | 1066 | /* disable ptp */ 1067 | rtl8125_clear_eth_phy_ocp_bit(tp, PTP_SYNCE_CTL, BIT_0); 1068 | rtl8125_clear_eth_phy_ocp_bit(tp, PTP_CTL, BIT_0); 1069 | rtl8125_set_eth_phy_ocp_bit(tp, 0xA640, BIT_15); 1070 | } 1071 | 1072 | r8125_spin_unlock(&tp->phy_lock, flags); 1073 | 1074 | return 0; 1075 | } 1076 | 1077 | void rtl8125_set_phy_local_time(struct rtl8125_private *tp) 1078 | { 1079 | struct timespec64 ts64; 1080 | //set system time 1081 | ktime_get_real_ts64(&ts64); 1082 | _rtl8125_phy_phc_settime(tp, &ts64); 1083 | } 1084 | 1085 | static long rtl8125_ptp_create_clock(struct rtl8125_private *tp) 1086 | { 1087 | struct net_device *netdev = tp->dev; 1088 | long err; 1089 | 1090 | if (!IS_ERR_OR_NULL(tp->ptp_clock)) 1091 | return 0; 1092 | 1093 | if (tp->HwSuppPtpVer == 0) { 1094 | tp->ptp_clock = NULL; 1095 | return -EOPNOTSUPP; 1096 | } 1097 | 1098 | switch (tp->HwSuppPtpVer) { 1099 | case 1: 1100 | tp->ptp_clock_info = rtl8125_mac_ptp_clock_info; 1101 | tp->ptp_clock_info.max_adj = 119304647; 1102 | break; 1103 | case 3: 1104 | tp->ptp_clock_info = rtl8125_phy_ptp_clock_info; 1105 | tp->ptp_clock_info.max_adj = 488281;//0x1FFFFF * 10^9 / 2^32 1106 | break; 1107 | default: 1108 | break; 1109 | } 1110 | 1111 | snprintf(tp->ptp_clock_info.name, sizeof(tp->ptp_clock_info.name), 1112 | "%pm", tp->dev->dev_addr); 1113 | tp->ptp_clock = ptp_clock_register(&tp->ptp_clock_info, &tp->pci_dev->dev); 1114 | if (IS_ERR(tp->ptp_clock)) { 1115 | err = PTR_ERR(tp->ptp_clock); 1116 | tp->ptp_clock = NULL; 1117 | netif_err(tp, drv, tp->dev, "ptp_clock_register failed\n"); 1118 | return err; 1119 | } else 1120 | netif_info(tp, drv, tp->dev, "registered PHC device on %s\n", netdev->name); 1121 | 1122 | return 0; 1123 | } 1124 | 1125 | void rtl8125_ptp_reset(struct rtl8125_private *tp) 1126 | { 1127 | if (!tp->ptp_clock) 1128 | return; 1129 | 1130 | netif_info(tp, drv, tp->dev, "reset PHC clock\n"); 1131 | 1132 | switch (tp->HwSuppPtpVer) { 1133 | case 1: 1134 | rtl8125_mac_hwtstamp_enable(tp, false); 1135 | break; 1136 | case 3: 1137 | rtl8125_phy_hwtstamp_enable(tp, false); 1138 | break; 1139 | default: 1140 | break; 1141 | } 1142 | } 1143 | 1144 | static enum hrtimer_restart 1145 | rtl8125_phy_hrtimer_for_pps(struct hrtimer *timer) { 1146 | struct rtl8125_private *tp = container_of(timer, struct rtl8125_private, pps_timer); 1147 | s64 pps_sec; 1148 | u16 tai_cfg; 1149 | int i; 1150 | 1151 | if (tp->pps_enable) 1152 | { 1153 | switch (tp->HwSuppPtpVer) { 1154 | case 3: 1155 | tai_cfg = BIT_8 | BIT_5 | BIT_1 | BIT_0; 1156 | break; 1157 | default: 1158 | break; 1159 | } 1160 | 1161 | //Direct Read 1162 | rtl8125_clear_and_set_eth_phy_ocp_bit(tp, 1163 | PTP_CLK_CFG_8126, 1164 | BIT_3 | BIT_2 | BIT_1, 1165 | (PTP_CLKADJ_MODE_SET | DIRECT_READ)); 1166 | 1167 | for (i = 0; i < R8125_CHANNEL_WAIT_COUNT; i++) { 1168 | udelay(R8125_CHANNEL_WAIT_TIME); 1169 | 1170 | if (!(rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CLK_CFG_8126) & PTP_CLKADJ_MODE_SET)) 1171 | break; 1172 | } 1173 | 1174 | pps_sec = rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_HI_8126); 1175 | pps_sec <<= 16; 1176 | pps_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_MI_8126); 1177 | pps_sec <<= 16; 1178 | pps_sec |= rtl8125_mdio_direct_read_phy_ocp(tp, PTP_CFG_S_LO_8126); 1179 | pps_sec++; 1180 | 1181 | //E42A[15:0] 1182 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_TAI_TS_S_LO, pps_sec & 0xffff); 1183 | //E42C[31:16] 1184 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_TAI_TS_S_HI, (pps_sec & 0xffff0000) >> 16); 1185 | //Periodic Tai start 1186 | rtl8125_mdio_direct_write_phy_ocp(tp, PTP_TAI_CFG, tai_cfg); 1187 | 1188 | hrtimer_forward_now(&tp->pps_timer, 1000000000); //rekick 1189 | return HRTIMER_RESTART; 1190 | } else 1191 | return HRTIMER_NORESTART; 1192 | } 1193 | 1194 | void rtl8125_ptp_init(struct rtl8125_private *tp) 1195 | { 1196 | /* obtain a PTP device, or re-use an existing device */ 1197 | if (rtl8125_ptp_create_clock(tp)) 1198 | return; 1199 | 1200 | /* we have a clock so we can initialize work now */ 1201 | switch (tp->HwSuppPtpVer) { 1202 | case 1: 1203 | INIT_WORK(&tp->ptp_tx_work, rtl8125_mac_ptp_tx_work); 1204 | break; 1205 | case 3: 1206 | INIT_WORK(&tp->ptp_tx_work, rtl8125_phy_ptp_tx_work); 1207 | break; 1208 | default: 1209 | break; 1210 | } 1211 | 1212 | /* init a hrtimer for pps */ 1213 | switch (tp->HwSuppPtpVer) { 1214 | case 3: 1215 | tp->pps_enable = 0; 1216 | hrtimer_init(&tp->pps_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 1217 | tp->pps_timer.function = rtl8125_phy_hrtimer_for_pps; 1218 | break; 1219 | default: 1220 | break; 1221 | } 1222 | 1223 | /* reset the PTP related hardware bits */ 1224 | rtl8125_ptp_reset(tp); 1225 | 1226 | return; 1227 | } 1228 | 1229 | void rtl8125_ptp_suspend(struct rtl8125_private *tp) 1230 | { 1231 | if (!tp->ptp_clock) 1232 | return; 1233 | 1234 | netif_info(tp, drv, tp->dev, "suspend PHC clock\n"); 1235 | 1236 | switch (tp->HwSuppPtpVer) { 1237 | case 1: 1238 | rtl8125_mac_hwtstamp_enable(tp, false); 1239 | break; 1240 | case 3: 1241 | rtl8125_phy_hwtstamp_enable(tp, false); 1242 | break; 1243 | default: 1244 | break; 1245 | } 1246 | 1247 | /* ensure that we cancel any pending PTP Tx work item in progress */ 1248 | cancel_work_sync(&tp->ptp_tx_work); 1249 | 1250 | switch (tp->HwSuppPtpVer) { 1251 | case 3: 1252 | hrtimer_cancel(&tp->pps_timer); 1253 | break; 1254 | default: 1255 | break; 1256 | } 1257 | } 1258 | 1259 | void rtl8125_ptp_stop(struct rtl8125_private *tp) 1260 | { 1261 | struct net_device *netdev = tp->dev; 1262 | 1263 | netif_info(tp, drv, tp->dev, "stop PHC clock\n"); 1264 | 1265 | /* first, suspend PTP activity */ 1266 | rtl8125_ptp_suspend(tp); 1267 | 1268 | /* disable the PTP clock device */ 1269 | if (tp->ptp_clock) { 1270 | ptp_clock_unregister(tp->ptp_clock); 1271 | tp->ptp_clock = NULL; 1272 | netif_info(tp, drv, tp->dev, "removed PHC on %s\n", 1273 | netdev->name); 1274 | } 1275 | } 1276 | 1277 | static int rtl8125_set_tstamp(struct net_device *netdev, struct ifreq *ifr) 1278 | { 1279 | struct rtl8125_private *tp = netdev_priv(netdev); 1280 | struct hwtstamp_config config; 1281 | bool hwtstamp = 0; 1282 | 1283 | //netif_info(tp, drv, tp->dev, "ptp set ts\n"); 1284 | 1285 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 1286 | return -EFAULT; 1287 | 1288 | if (config.flags) 1289 | return -EINVAL; 1290 | 1291 | switch (config.tx_type) { 1292 | case HWTSTAMP_TX_ON: 1293 | hwtstamp = 1; 1294 | break; 1295 | case HWTSTAMP_TX_OFF: 1296 | break; 1297 | case HWTSTAMP_TX_ONESTEP_SYNC: 1298 | default: 1299 | return -ERANGE; 1300 | } 1301 | 1302 | switch (config.rx_filter) { 1303 | case HWTSTAMP_FILTER_PTP_V2_EVENT: 1304 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 1305 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 1306 | case HWTSTAMP_FILTER_PTP_V2_SYNC: 1307 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 1308 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 1309 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 1310 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 1311 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 1312 | config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 1313 | hwtstamp = 1; 1314 | tp->flags |= RTL_FLAG_RX_HWTSTAMP_ENABLED; 1315 | break; 1316 | case HWTSTAMP_FILTER_NONE: 1317 | tp->flags &= ~RTL_FLAG_RX_HWTSTAMP_ENABLED; 1318 | break; 1319 | default: 1320 | tp->flags &= ~RTL_FLAG_RX_HWTSTAMP_ENABLED; 1321 | return -ERANGE; 1322 | } 1323 | 1324 | if (tp->hwtstamp_config.tx_type != config.tx_type || 1325 | tp->hwtstamp_config.rx_filter != config.rx_filter) { 1326 | tp->hwtstamp_config = config; 1327 | 1328 | switch (tp->HwSuppPtpVer) { 1329 | case 1: 1330 | rtl8125_mac_hwtstamp_enable(tp, hwtstamp); 1331 | break; 1332 | case 3: 1333 | rtl8125_phy_hwtstamp_enable(tp, hwtstamp); 1334 | break; 1335 | default: 1336 | break; 1337 | } 1338 | } 1339 | 1340 | return copy_to_user(ifr->ifr_data, &config, 1341 | sizeof(config)) ? -EFAULT : 0; 1342 | } 1343 | 1344 | static int rtl8125_get_tstamp(struct net_device *netdev, struct ifreq *ifr) 1345 | { 1346 | struct rtl8125_private *tp = netdev_priv(netdev); 1347 | 1348 | //netif_info(tp, drv, tp->dev, "ptp get ts\n"); 1349 | 1350 | return copy_to_user(ifr->ifr_data, &tp->hwtstamp_config, 1351 | sizeof(tp->hwtstamp_config)) ? -EFAULT : 0; 1352 | } 1353 | 1354 | int rtl8125_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 1355 | { 1356 | int ret; 1357 | 1358 | //netif_info(tp, drv, tp->dev, "ptp ioctl\n"); 1359 | 1360 | switch (cmd) { 1361 | #ifdef ENABLE_PTP_SUPPORT 1362 | case SIOCSHWTSTAMP: 1363 | ret = rtl8125_set_tstamp(netdev, ifr); 1364 | break; 1365 | case SIOCGHWTSTAMP: 1366 | ret = rtl8125_get_tstamp(netdev, ifr); 1367 | break; 1368 | #endif 1369 | default: 1370 | ret = -EOPNOTSUPP; 1371 | break; 1372 | } 1373 | 1374 | return ret; 1375 | } 1376 | 1377 | void rtl8125_rx_mac_ptp_pktstamp(struct rtl8125_private *tp, struct sk_buff *skb, 1378 | struct RxDescV3 *descv3) 1379 | { 1380 | time64_t tv_sec; 1381 | long tv_nsec; 1382 | 1383 | tv_sec = le32_to_cpu(descv3->RxDescTimeStamp.TimeStampHigh) + 1384 | ((u64)le32_to_cpu(descv3->RxDescPTPDDWord4.TimeStampHHigh) << 32); 1385 | tv_nsec = le32_to_cpu(descv3->RxDescTimeStamp.TimeStampLow); 1386 | 1387 | skb_hwtstamps(skb)->hwtstamp = ktime_set(tv_sec, tv_nsec); 1388 | } 1389 | 1390 | static void rtl8125_rx_phy_ptp_pktstamp(struct rtl8125_private *tp, struct sk_buff *skb, u8 type) 1391 | { 1392 | struct timespec64 ts64; 1393 | unsigned long flags; 1394 | 1395 | r8125_spin_lock(&tp->phy_lock, flags); 1396 | 1397 | rtl8125_phy_ptp_ingresstime(tp, &ts64, type); 1398 | 1399 | r8125_spin_unlock(&tp->phy_lock, flags); 1400 | 1401 | skb_hwtstamps(skb)->hwtstamp = ktime_set(ts64.tv_sec, ts64.tv_nsec); 1402 | 1403 | return; 1404 | } 1405 | 1406 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) 1407 | static struct ptp_header *ptp_parse_header(struct sk_buff *skb, unsigned int type) 1408 | { 1409 | u8 *ptr = skb_mac_header(skb); 1410 | 1411 | if (type & PTP_CLASS_VLAN) 1412 | //ptr += VLAN_HLEN; 1413 | ptr += 4; 1414 | 1415 | switch (type & PTP_CLASS_PMASK) { 1416 | case PTP_CLASS_IPV4: 1417 | ptr += IPV4_HLEN(ptr) + UDP_HLEN; 1418 | break; 1419 | case PTP_CLASS_IPV6: 1420 | ptr += IP6_HLEN + UDP_HLEN; 1421 | break; 1422 | case PTP_CLASS_L2: 1423 | break; 1424 | default: 1425 | return NULL; 1426 | } 1427 | 1428 | ptr += ETH_HLEN; 1429 | 1430 | /* Ensure that the entire header is present in this packet. */ 1431 | if (ptr + sizeof(struct ptp_header) > skb->data + skb->len) 1432 | return NULL; 1433 | 1434 | return (struct ptp_header *)ptr; 1435 | } 1436 | 1437 | static inline u8 ptp_get_msgtype(const struct ptp_header *hdr, 1438 | unsigned int type) 1439 | { 1440 | u8 msgtype; 1441 | 1442 | if (unlikely(type & PTP_CLASS_V1)) { 1443 | /* msg type is located at the control field for ptp v1 */ 1444 | msgtype = hdr->control; 1445 | } else { 1446 | msgtype = hdr->tsmt & 0x0f; 1447 | } 1448 | 1449 | return msgtype; 1450 | } 1451 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) */ 1452 | 1453 | void rtl8125_rx_phy_ptp_timestamp(struct rtl8125_private *tp, struct sk_buff *skb) 1454 | { 1455 | unsigned int ptp_class; 1456 | struct ptp_header *hdr; 1457 | u8 msgtype; 1458 | 1459 | ptp_class = ptp_classify_raw(skb); 1460 | if (ptp_class == PTP_CLASS_NONE) 1461 | return; 1462 | 1463 | skb_reset_mac_header(skb); 1464 | hdr = ptp_parse_header(skb, ptp_class); 1465 | if (unlikely(!hdr)) 1466 | return; 1467 | 1468 | msgtype = ptp_get_msgtype(hdr, ptp_class); 1469 | rtl8125_rx_phy_ptp_pktstamp(tp, skb, msgtype); 1470 | 1471 | return; 1472 | } 1473 | --------------------------------------------------------------------------------