├── 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 |
--------------------------------------------------------------------------------