1e6ad7673SIyappan Subramanian /* Applied Micro X-Gene SoC Ethernet Driver
2e6ad7673SIyappan Subramanian  *
3e6ad7673SIyappan Subramanian  * Copyright (c) 2014, Applied Micro Circuits Corporation
4e6ad7673SIyappan Subramanian  * Authors: Iyappan Subramanian <isubramanian@apm.com>
5e6ad7673SIyappan Subramanian  *
6e6ad7673SIyappan Subramanian  * This program is free software; you can redistribute  it and/or modify it
7e6ad7673SIyappan Subramanian  * under  the terms of  the GNU General  Public License as published by the
8e6ad7673SIyappan Subramanian  * Free Software Foundation;  either version 2 of the  License, or (at your
9e6ad7673SIyappan Subramanian  * option) any later version.
10e6ad7673SIyappan Subramanian  *
11e6ad7673SIyappan Subramanian  * This program is distributed in the hope that it will be useful,
12e6ad7673SIyappan Subramanian  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13e6ad7673SIyappan Subramanian  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14e6ad7673SIyappan Subramanian  * GNU General Public License for more details.
15e6ad7673SIyappan Subramanian  *
16e6ad7673SIyappan Subramanian  * You should have received a copy of the GNU General Public License
17e6ad7673SIyappan Subramanian  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18e6ad7673SIyappan Subramanian  */
19e6ad7673SIyappan Subramanian 
20e6ad7673SIyappan Subramanian #include <linux/ethtool.h>
21e6ad7673SIyappan Subramanian #include "xgene_enet_main.h"
22e6ad7673SIyappan Subramanian 
23e6ad7673SIyappan Subramanian struct xgene_gstrings_stats {
24e6ad7673SIyappan Subramanian 	char name[ETH_GSTRING_LEN];
25e6ad7673SIyappan Subramanian 	int offset;
262d07d8e4SQuan Nguyen 	u32 addr;
272d07d8e4SQuan Nguyen 	u32 mask;
28e6ad7673SIyappan Subramanian };
29e6ad7673SIyappan Subramanian 
303f5a2ef1SQuan Nguyen #define XGENE_STAT(m) { #m, offsetof(struct rtnl_link_stats64, m) }
312d07d8e4SQuan Nguyen #define XGENE_EXTD_STAT(s, a, m)		\
322d07d8e4SQuan Nguyen 		{			\
332d07d8e4SQuan Nguyen 		.name = #s,		\
342d07d8e4SQuan Nguyen 		.addr = a ## _ADDR,	\
352d07d8e4SQuan Nguyen 		.mask = m		\
362d07d8e4SQuan Nguyen 		}
37e6ad7673SIyappan Subramanian 
38e6ad7673SIyappan Subramanian static const struct xgene_gstrings_stats gstrings_stats[] = {
39e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_packets),
40e6ad7673SIyappan Subramanian 	XGENE_STAT(tx_packets),
41e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_bytes),
42e6ad7673SIyappan Subramanian 	XGENE_STAT(tx_bytes),
43e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_errors),
44e6ad7673SIyappan Subramanian 	XGENE_STAT(tx_errors),
45e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_length_errors),
46e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_crc_errors),
47e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_frame_errors),
48e6ad7673SIyappan Subramanian 	XGENE_STAT(rx_fifo_errors)
49e6ad7673SIyappan Subramanian };
50e6ad7673SIyappan Subramanian 
512d07d8e4SQuan Nguyen static const struct xgene_gstrings_stats gstrings_extd_stats[] = {
522d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64, 31),
532d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127, 31),
542d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255, 31),
552d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511, 31),
562d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K, 31),
572d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX, 31),
582d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV, 31),
592d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_fcs_error_cntr, RFCS, 16),
602d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA, 31),
612d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA, 31),
622d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF, 16),
632d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF, 16),
642d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_unk_opcode_cntr, RXUO, 16),
652d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_align_err_cntr, RALN, 16),
662d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_frame_len_err_cntr, RFLR, 16),
67eaef62a4SQuan Nguyen 	XGENE_EXTD_STAT(rx_frame_len_err_recov_cntr, DUMP, 0),
682d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16),
692d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16),
702d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16),
712d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16),
722d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16),
732d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16),
7461c759cdSQuan Nguyen 	XGENE_EXTD_STAT(rx_jabber_recov_cntr, DUMP, 0),
752d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16),
76ca6d550cSIyappan Subramanian 	XGENE_EXTD_STAT(rx_overrun_cntr, DUMP, 0),
772d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31),
782d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31),
792d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16),
802d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31),
812d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31),
822d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31),
832d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31),
842d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31),
852d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31),
862d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31),
872d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16),
882d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16),
892d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12),
902d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12),
912d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12),
922d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12),
932d07d8e4SQuan Nguyen 	XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12),
94ca6d550cSIyappan Subramanian 	XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12),
95ca6d550cSIyappan Subramanian 	XGENE_EXTD_STAT(tx_underrun_cntr, DUMP, 0)
962d07d8e4SQuan Nguyen };
972d07d8e4SQuan Nguyen 
98e6ad7673SIyappan Subramanian #define XGENE_STATS_LEN		ARRAY_SIZE(gstrings_stats)
992d07d8e4SQuan Nguyen #define XGENE_EXTD_STATS_LEN	ARRAY_SIZE(gstrings_extd_stats)
10061c759cdSQuan Nguyen #define RFCS_IDX		7
10161c759cdSQuan Nguyen #define RALN_IDX		13
10261c759cdSQuan Nguyen #define RFLR_IDX		14
103eaef62a4SQuan Nguyen #define FALSE_RFLR_IDX		15
10461c759cdSQuan Nguyen #define RUND_IDX		18
10561c759cdSQuan Nguyen #define FALSE_RJBR_IDX		22
10661c759cdSQuan Nguyen #define RX_OVERRUN_IDX		24
10761c759cdSQuan Nguyen #define TFCS_IDX		38
10861c759cdSQuan Nguyen #define TFRG_IDX		42
10961c759cdSQuan Nguyen #define TX_UNDERRUN_IDX		43
110e6ad7673SIyappan Subramanian 
111e6ad7673SIyappan Subramanian static void xgene_get_drvinfo(struct net_device *ndev,
112e6ad7673SIyappan Subramanian 			      struct ethtool_drvinfo *info)
113e6ad7673SIyappan Subramanian {
114e6ad7673SIyappan Subramanian 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
115e6ad7673SIyappan Subramanian 	struct platform_device *pdev = pdata->pdev;
116e6ad7673SIyappan Subramanian 
117e6ad7673SIyappan Subramanian 	strcpy(info->driver, "xgene_enet");
118e6ad7673SIyappan Subramanian 	strcpy(info->version, XGENE_DRV_VERSION);
119e6ad7673SIyappan Subramanian 	snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A");
120e6ad7673SIyappan Subramanian 	sprintf(info->bus_info, "%s", pdev->name);
121e6ad7673SIyappan Subramanian }
122e6ad7673SIyappan Subramanian 
12336a19b29SPhilippe Reynes static int xgene_get_link_ksettings(struct net_device *ndev,
12436a19b29SPhilippe Reynes 				    struct ethtool_link_ksettings *cmd)
125e6ad7673SIyappan Subramanian {
126e6ad7673SIyappan Subramanian 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
127971d3a44SPhilippe Reynes 	struct phy_device *phydev = ndev->phydev;
12836a19b29SPhilippe Reynes 	u32 supported;
129e6ad7673SIyappan Subramanian 
130326dde3eSIyappan Subramanian 	if (phy_interface_mode_is_rgmii(pdata->phy_mode)) {
131e6ad7673SIyappan Subramanian 		if (phydev == NULL)
132e6ad7673SIyappan Subramanian 			return -ENODEV;
133e6ad7673SIyappan Subramanian 
1345514174fSyuval.shaia@oracle.com 		phy_ethtool_ksettings_get(phydev, cmd);
1355514174fSyuval.shaia@oracle.com 
1365514174fSyuval.shaia@oracle.com 		return 0;
1375e6a024bSIyappan Subramanian 	} else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
13852d1fd99SIyappan Subramanian 		if (pdata->mdio_driver) {
13952d1fd99SIyappan Subramanian 			if (!phydev)
14052d1fd99SIyappan Subramanian 				return -ENODEV;
14152d1fd99SIyappan Subramanian 
1425514174fSyuval.shaia@oracle.com 			phy_ethtool_ksettings_get(phydev, cmd);
1435514174fSyuval.shaia@oracle.com 
1445514174fSyuval.shaia@oracle.com 			return 0;
14552d1fd99SIyappan Subramanian 		}
14652d1fd99SIyappan Subramanian 
14736a19b29SPhilippe Reynes 		supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
14852d1fd99SIyappan Subramanian 			SUPPORTED_MII;
14936a19b29SPhilippe Reynes 		ethtool_convert_legacy_u32_to_link_mode(
15036a19b29SPhilippe Reynes 			cmd->link_modes.supported,
15136a19b29SPhilippe Reynes 			supported);
15236a19b29SPhilippe Reynes 		ethtool_convert_legacy_u32_to_link_mode(
15336a19b29SPhilippe Reynes 			cmd->link_modes.advertising,
15436a19b29SPhilippe Reynes 			supported);
15536a19b29SPhilippe Reynes 
15636a19b29SPhilippe Reynes 		cmd->base.speed = SPEED_1000;
15736a19b29SPhilippe Reynes 		cmd->base.duplex = DUPLEX_FULL;
15836a19b29SPhilippe Reynes 		cmd->base.port = PORT_MII;
15936a19b29SPhilippe Reynes 		cmd->base.autoneg = AUTONEG_ENABLE;
1605e6a024bSIyappan Subramanian 	} else {
16136a19b29SPhilippe Reynes 		supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE;
16236a19b29SPhilippe Reynes 		ethtool_convert_legacy_u32_to_link_mode(
16336a19b29SPhilippe Reynes 			cmd->link_modes.supported,
16436a19b29SPhilippe Reynes 			supported);
16536a19b29SPhilippe Reynes 		ethtool_convert_legacy_u32_to_link_mode(
16636a19b29SPhilippe Reynes 			cmd->link_modes.advertising,
16736a19b29SPhilippe Reynes 			supported);
16836a19b29SPhilippe Reynes 
16936a19b29SPhilippe Reynes 		cmd->base.speed = SPEED_10000;
17036a19b29SPhilippe Reynes 		cmd->base.duplex = DUPLEX_FULL;
17136a19b29SPhilippe Reynes 		cmd->base.port = PORT_FIBRE;
17236a19b29SPhilippe Reynes 		cmd->base.autoneg = AUTONEG_DISABLE;
1735e6a024bSIyappan Subramanian 	}
17441aace6eSIyappan Subramanian 
17541aace6eSIyappan Subramanian 	return 0;
17641aace6eSIyappan Subramanian }
17741aace6eSIyappan Subramanian 
17836a19b29SPhilippe Reynes static int xgene_set_link_ksettings(struct net_device *ndev,
17936a19b29SPhilippe Reynes 				    const struct ethtool_link_ksettings *cmd)
180e6ad7673SIyappan Subramanian {
181e6ad7673SIyappan Subramanian 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
182971d3a44SPhilippe Reynes 	struct phy_device *phydev = ndev->phydev;
183e6ad7673SIyappan Subramanian 
184326dde3eSIyappan Subramanian 	if (phy_interface_mode_is_rgmii(pdata->phy_mode)) {
18552d1fd99SIyappan Subramanian 		if (!phydev)
186e6ad7673SIyappan Subramanian 			return -ENODEV;
187e6ad7673SIyappan Subramanian 
18836a19b29SPhilippe Reynes 		return phy_ethtool_ksettings_set(phydev, cmd);
189e6ad7673SIyappan Subramanian 	}
190e6ad7673SIyappan Subramanian 
19152d1fd99SIyappan Subramanian 	if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
19252d1fd99SIyappan Subramanian 		if (pdata->mdio_driver) {
19352d1fd99SIyappan Subramanian 			if (!phydev)
19452d1fd99SIyappan Subramanian 				return -ENODEV;
19552d1fd99SIyappan Subramanian 
19636a19b29SPhilippe Reynes 			return phy_ethtool_ksettings_set(phydev, cmd);
19752d1fd99SIyappan Subramanian 		}
19852d1fd99SIyappan Subramanian 	}
19952d1fd99SIyappan Subramanian 
20041aace6eSIyappan Subramanian 	return -EINVAL;
20141aace6eSIyappan Subramanian }
20241aace6eSIyappan Subramanian 
203e6ad7673SIyappan Subramanian static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
204e6ad7673SIyappan Subramanian {
205e6ad7673SIyappan Subramanian 	int i;
206e6ad7673SIyappan Subramanian 	u8 *p = data;
207e6ad7673SIyappan Subramanian 
208e6ad7673SIyappan Subramanian 	if (stringset != ETH_SS_STATS)
209e6ad7673SIyappan Subramanian 		return;
210e6ad7673SIyappan Subramanian 
211e6ad7673SIyappan Subramanian 	for (i = 0; i < XGENE_STATS_LEN; i++) {
212e6ad7673SIyappan Subramanian 		memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN);
213e6ad7673SIyappan Subramanian 		p += ETH_GSTRING_LEN;
214e6ad7673SIyappan Subramanian 	}
2152d07d8e4SQuan Nguyen 
2162d07d8e4SQuan Nguyen 	for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) {
2172d07d8e4SQuan Nguyen 		memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN);
2182d07d8e4SQuan Nguyen 		p += ETH_GSTRING_LEN;
2192d07d8e4SQuan Nguyen 	}
220e6ad7673SIyappan Subramanian }
221e6ad7673SIyappan Subramanian 
222e6ad7673SIyappan Subramanian static int xgene_get_sset_count(struct net_device *ndev, int sset)
223e6ad7673SIyappan Subramanian {
224e6ad7673SIyappan Subramanian 	if (sset != ETH_SS_STATS)
225e6ad7673SIyappan Subramanian 		return -EINVAL;
226e6ad7673SIyappan Subramanian 
2272d07d8e4SQuan Nguyen 	return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN;
2282d07d8e4SQuan Nguyen }
2292d07d8e4SQuan Nguyen 
2302d07d8e4SQuan Nguyen static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata)
2312d07d8e4SQuan Nguyen {
232ca6d550cSIyappan Subramanian 	u32 rx_drop, tx_drop;
23361c759cdSQuan Nguyen 	u32 mask, tmp;
2342d07d8e4SQuan Nguyen 	int i;
2352d07d8e4SQuan Nguyen 
2362d07d8e4SQuan Nguyen 	for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) {
2372d07d8e4SQuan Nguyen 		tmp = xgene_enet_rd_stat(pdata, gstrings_extd_stats[i].addr);
23861c759cdSQuan Nguyen 		if (gstrings_extd_stats[i].mask) {
23961c759cdSQuan Nguyen 			mask = GENMASK(gstrings_extd_stats[i].mask - 1, 0);
24061c759cdSQuan Nguyen 			pdata->extd_stats[i] += (tmp & mask);
24161c759cdSQuan Nguyen 		}
24261c759cdSQuan Nguyen 	}
24361c759cdSQuan Nguyen 
24461c759cdSQuan Nguyen 	if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
24561c759cdSQuan Nguyen 		/* Errata 10GE_10 - SW should intepret RALN as 0 */
24661c759cdSQuan Nguyen 		pdata->extd_stats[RALN_IDX] = 0;
24761c759cdSQuan Nguyen 	} else {
24861c759cdSQuan Nguyen 		/* Errata ENET_15 - Fixes RFCS, RFLR, TFCS counter */
24961c759cdSQuan Nguyen 		pdata->extd_stats[RFCS_IDX] -= pdata->extd_stats[RALN_IDX];
25061c759cdSQuan Nguyen 		pdata->extd_stats[RFLR_IDX] -= pdata->extd_stats[RUND_IDX];
25161c759cdSQuan Nguyen 		pdata->extd_stats[TFCS_IDX] -= pdata->extd_stats[TFRG_IDX];
2522d07d8e4SQuan Nguyen 	}
253ca6d550cSIyappan Subramanian 
254ca6d550cSIyappan Subramanian 	pdata->mac_ops->get_drop_cnt(pdata, &rx_drop, &tx_drop);
255ca6d550cSIyappan Subramanian 	pdata->extd_stats[RX_OVERRUN_IDX] += rx_drop;
256ca6d550cSIyappan Subramanian 	pdata->extd_stats[TX_UNDERRUN_IDX] += tx_drop;
257eaef62a4SQuan Nguyen 
258eaef62a4SQuan Nguyen 	/* Errata 10GE_8 -  Update Frame recovered from Errata 10GE_8/ENET_11 */
259eaef62a4SQuan Nguyen 	pdata->extd_stats[FALSE_RFLR_IDX] = pdata->false_rflr;
26061c759cdSQuan Nguyen 	/* Errata ENET_15 - Jabber Frame recov'ed from Errata 10GE_10/ENET_15 */
26161c759cdSQuan Nguyen 	pdata->extd_stats[FALSE_RJBR_IDX] = pdata->vlan_rjbr;
2622d07d8e4SQuan Nguyen }
2632d07d8e4SQuan Nguyen 
2642d07d8e4SQuan Nguyen int xgene_extd_stats_init(struct xgene_enet_pdata *pdata)
2652d07d8e4SQuan Nguyen {
2662d07d8e4SQuan Nguyen 	pdata->extd_stats = devm_kmalloc_array(&pdata->pdev->dev,
2672d07d8e4SQuan Nguyen 			XGENE_EXTD_STATS_LEN, sizeof(u64), GFP_KERNEL);
2682d07d8e4SQuan Nguyen 	if (!pdata->extd_stats)
2692d07d8e4SQuan Nguyen 		return -ENOMEM;
2702d07d8e4SQuan Nguyen 
2712d07d8e4SQuan Nguyen 	xgene_get_extd_stats(pdata);
2722d07d8e4SQuan Nguyen 	memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64));
2732d07d8e4SQuan Nguyen 
2742d07d8e4SQuan Nguyen 	return 0;
275e6ad7673SIyappan Subramanian }
276e6ad7673SIyappan Subramanian 
277e6ad7673SIyappan Subramanian static void xgene_get_ethtool_stats(struct net_device *ndev,
278e6ad7673SIyappan Subramanian 				    struct ethtool_stats *dummy,
279e6ad7673SIyappan Subramanian 				    u64 *data)
280e6ad7673SIyappan Subramanian {
2812d07d8e4SQuan Nguyen 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
2823f5a2ef1SQuan Nguyen 	struct rtnl_link_stats64 stats;
283e6ad7673SIyappan Subramanian 	int i;
284e6ad7673SIyappan Subramanian 
2853f5a2ef1SQuan Nguyen 	dev_get_stats(ndev, &stats);
286e6ad7673SIyappan Subramanian 	for (i = 0; i < XGENE_STATS_LEN; i++)
2873f5a2ef1SQuan Nguyen 		data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset);
2882d07d8e4SQuan Nguyen 
2892d07d8e4SQuan Nguyen 	xgene_get_extd_stats(pdata);
2902d07d8e4SQuan Nguyen 	for (i = 0; i < XGENE_EXTD_STATS_LEN; i++)
2912d07d8e4SQuan Nguyen 		data[i + XGENE_STATS_LEN] = pdata->extd_stats[i];
292e6ad7673SIyappan Subramanian }
293e6ad7673SIyappan Subramanian 
2940296fe4dSIyappan Subramanian static void xgene_get_pauseparam(struct net_device *ndev,
2950296fe4dSIyappan Subramanian 				 struct ethtool_pauseparam *pp)
2960296fe4dSIyappan Subramanian {
2970296fe4dSIyappan Subramanian 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
2980296fe4dSIyappan Subramanian 
2990296fe4dSIyappan Subramanian 	pp->autoneg = pdata->pause_autoneg;
3000296fe4dSIyappan Subramanian 	pp->tx_pause = pdata->tx_pause;
3010296fe4dSIyappan Subramanian 	pp->rx_pause = pdata->rx_pause;
3020296fe4dSIyappan Subramanian }
3030296fe4dSIyappan Subramanian 
3040296fe4dSIyappan Subramanian static int xgene_set_pauseparam(struct net_device *ndev,
3050296fe4dSIyappan Subramanian 				struct ethtool_pauseparam *pp)
3060296fe4dSIyappan Subramanian {
3070296fe4dSIyappan Subramanian 	struct xgene_enet_pdata *pdata = netdev_priv(ndev);
3080296fe4dSIyappan Subramanian 	struct phy_device *phydev = ndev->phydev;
3090296fe4dSIyappan Subramanian 	u32 oldadv, newadv;
3100296fe4dSIyappan Subramanian 
311326dde3eSIyappan Subramanian 	if (phy_interface_mode_is_rgmii(pdata->phy_mode) ||
3120296fe4dSIyappan Subramanian 	    pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
3130296fe4dSIyappan Subramanian 		if (!phydev)
3140296fe4dSIyappan Subramanian 			return -EINVAL;
3150296fe4dSIyappan Subramanian 
3160296fe4dSIyappan Subramanian 		if (!(phydev->supported & SUPPORTED_Pause) ||
3170296fe4dSIyappan Subramanian 		    (!(phydev->supported & SUPPORTED_Asym_Pause) &&
3180296fe4dSIyappan Subramanian 		     pp->rx_pause != pp->tx_pause))
3190296fe4dSIyappan Subramanian 			return -EINVAL;
3200296fe4dSIyappan Subramanian 
3210296fe4dSIyappan Subramanian 		pdata->pause_autoneg = pp->autoneg;
3220296fe4dSIyappan Subramanian 		pdata->tx_pause = pp->tx_pause;
3230296fe4dSIyappan Subramanian 		pdata->rx_pause = pp->rx_pause;
3240296fe4dSIyappan Subramanian 
3250296fe4dSIyappan Subramanian 		oldadv = phydev->advertising;
3260296fe4dSIyappan Subramanian 		newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
3270296fe4dSIyappan Subramanian 
3280296fe4dSIyappan Subramanian 		if (pp->rx_pause)
3290296fe4dSIyappan Subramanian 			newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
3300296fe4dSIyappan Subramanian 
3310296fe4dSIyappan Subramanian 		if (pp->tx_pause)
3320296fe4dSIyappan Subramanian 			newadv ^= ADVERTISED_Asym_Pause;
3330296fe4dSIyappan Subramanian 
3340296fe4dSIyappan Subramanian 		if (oldadv ^ newadv) {
3350296fe4dSIyappan Subramanian 			phydev->advertising = newadv;
3360296fe4dSIyappan Subramanian 
3370296fe4dSIyappan Subramanian 			if (phydev->autoneg)
3380296fe4dSIyappan Subramanian 				return phy_start_aneg(phydev);
3390296fe4dSIyappan Subramanian 
3400296fe4dSIyappan Subramanian 			if (!pp->autoneg) {
3410296fe4dSIyappan Subramanian 				pdata->mac_ops->flowctl_tx(pdata,
3420296fe4dSIyappan Subramanian 							   pdata->tx_pause);
3430296fe4dSIyappan Subramanian 				pdata->mac_ops->flowctl_rx(pdata,
3440296fe4dSIyappan Subramanian 							   pdata->rx_pause);
3450296fe4dSIyappan Subramanian 			}
3460296fe4dSIyappan Subramanian 		}
3470296fe4dSIyappan Subramanian 
3480296fe4dSIyappan Subramanian 	} else {
3490296fe4dSIyappan Subramanian 		if (pp->autoneg)
3500296fe4dSIyappan Subramanian 			return -EINVAL;
3510296fe4dSIyappan Subramanian 
3520296fe4dSIyappan Subramanian 		pdata->tx_pause = pp->tx_pause;
3530296fe4dSIyappan Subramanian 		pdata->rx_pause = pp->rx_pause;
3540296fe4dSIyappan Subramanian 
3550296fe4dSIyappan Subramanian 		pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause);
3560296fe4dSIyappan Subramanian 		pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause);
3570296fe4dSIyappan Subramanian 	}
3580296fe4dSIyappan Subramanian 
3590296fe4dSIyappan Subramanian 	return 0;
3600296fe4dSIyappan Subramanian }
3610296fe4dSIyappan Subramanian 
362e6ad7673SIyappan Subramanian static const struct ethtool_ops xgene_ethtool_ops = {
363e6ad7673SIyappan Subramanian 	.get_drvinfo = xgene_get_drvinfo,
364e6ad7673SIyappan Subramanian 	.get_link = ethtool_op_get_link,
365e6ad7673SIyappan Subramanian 	.get_strings = xgene_get_strings,
366e6ad7673SIyappan Subramanian 	.get_sset_count = xgene_get_sset_count,
36736a19b29SPhilippe Reynes 	.get_ethtool_stats = xgene_get_ethtool_stats,
36836a19b29SPhilippe Reynes 	.get_link_ksettings = xgene_get_link_ksettings,
36936a19b29SPhilippe Reynes 	.set_link_ksettings = xgene_set_link_ksettings,
3700296fe4dSIyappan Subramanian 	.get_pauseparam = xgene_get_pauseparam,
3710296fe4dSIyappan Subramanian 	.set_pauseparam = xgene_set_pauseparam
372e6ad7673SIyappan Subramanian };
373e6ad7673SIyappan Subramanian 
374e6ad7673SIyappan Subramanian void xgene_enet_set_ethtool_ops(struct net_device *ndev)
375e6ad7673SIyappan Subramanian {
376e6ad7673SIyappan Subramanian 	ndev->ethtool_ops = &xgene_ethtool_ops;
377e6ad7673SIyappan Subramanian }
378