xref: /linux/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1b2b3212bSRazvan Stefanescu // SPDX-License-Identifier: GPL-2.0
2b2b3212bSRazvan Stefanescu /*
3b2b3212bSRazvan Stefanescu  * DPAA2 Ethernet Switch ethtool support
4b2b3212bSRazvan Stefanescu  *
5b2b3212bSRazvan Stefanescu  * Copyright 2014-2016 Freescale Semiconductor Inc.
6b2b3212bSRazvan Stefanescu  * Copyright 2017-2018 NXP
7b2b3212bSRazvan Stefanescu  *
8b2b3212bSRazvan Stefanescu  */
9b2b3212bSRazvan Stefanescu 
10cc69837fSJakub Kicinski #include <linux/ethtool.h>
11cc69837fSJakub Kicinski 
12f48298d3SIoana Ciornei #include "dpaa2-switch.h"
13b2b3212bSRazvan Stefanescu 
14b2b3212bSRazvan Stefanescu static struct {
15b2b3212bSRazvan Stefanescu 	enum dpsw_counter id;
16b2b3212bSRazvan Stefanescu 	char name[ETH_GSTRING_LEN];
175ad71958SIoana Ciornei } dpaa2_switch_ethtool_counters[] =  {
188581362dSIoana Ciornei 	{DPSW_CNT_ING_FRAME,		"[hw] rx frames"},
198581362dSIoana Ciornei 	{DPSW_CNT_ING_BYTE,		"[hw] rx bytes"},
208581362dSIoana Ciornei 	{DPSW_CNT_ING_FLTR_FRAME,	"[hw] rx filtered frames"},
218581362dSIoana Ciornei 	{DPSW_CNT_ING_FRAME_DISCARD,	"[hw] rx discarded frames"},
228581362dSIoana Ciornei 	{DPSW_CNT_ING_BCAST_FRAME,	"[hw] rx bcast frames"},
238581362dSIoana Ciornei 	{DPSW_CNT_ING_BCAST_BYTES,	"[hw] rx bcast bytes"},
248581362dSIoana Ciornei 	{DPSW_CNT_ING_MCAST_FRAME,	"[hw] rx mcast frames"},
258581362dSIoana Ciornei 	{DPSW_CNT_ING_MCAST_BYTE,	"[hw] rx mcast bytes"},
268581362dSIoana Ciornei 	{DPSW_CNT_EGR_FRAME,		"[hw] tx frames"},
278581362dSIoana Ciornei 	{DPSW_CNT_EGR_BYTE,		"[hw] tx bytes"},
288581362dSIoana Ciornei 	{DPSW_CNT_EGR_FRAME_DISCARD,	"[hw] tx discarded frames"},
298581362dSIoana Ciornei 	{DPSW_CNT_ING_NO_BUFF_DISCARD,	"[hw] rx nobuffer discards"},
30b2b3212bSRazvan Stefanescu };
31b2b3212bSRazvan Stefanescu 
325ad71958SIoana Ciornei #define DPAA2_SWITCH_NUM_COUNTERS	ARRAY_SIZE(dpaa2_switch_ethtool_counters)
33b2b3212bSRazvan Stefanescu 
dpaa2_switch_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * drvinfo)345ad71958SIoana Ciornei static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
35b2b3212bSRazvan Stefanescu 				     struct ethtool_drvinfo *drvinfo)
36b2b3212bSRazvan Stefanescu {
37b2b3212bSRazvan Stefanescu 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
38b2b3212bSRazvan Stefanescu 	u16 version_major, version_minor;
39b2b3212bSRazvan Stefanescu 	int err;
40b2b3212bSRazvan Stefanescu 
41a288a21eSKumar Kartikeya Dwivedi 	strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
42b2b3212bSRazvan Stefanescu 
43b2b3212bSRazvan Stefanescu 	err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
44b2b3212bSRazvan Stefanescu 				   &version_major,
45b2b3212bSRazvan Stefanescu 				   &version_minor);
46b2b3212bSRazvan Stefanescu 	if (err)
47a288a21eSKumar Kartikeya Dwivedi 		strscpy(drvinfo->fw_version, "N/A",
48b2b3212bSRazvan Stefanescu 			sizeof(drvinfo->fw_version));
49b2b3212bSRazvan Stefanescu 	else
50b2b3212bSRazvan Stefanescu 		snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
51b2b3212bSRazvan Stefanescu 			 "%u.%u", version_major, version_minor);
52b2b3212bSRazvan Stefanescu 
53a288a21eSKumar Kartikeya Dwivedi 	strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
54b2b3212bSRazvan Stefanescu 		sizeof(drvinfo->bus_info));
55b2b3212bSRazvan Stefanescu }
56b2b3212bSRazvan Stefanescu 
57b2b3212bSRazvan Stefanescu static int
dpaa2_switch_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * link_ksettings)585ad71958SIoana Ciornei dpaa2_switch_get_link_ksettings(struct net_device *netdev,
59b2b3212bSRazvan Stefanescu 				struct ethtool_link_ksettings *link_ksettings)
60b2b3212bSRazvan Stefanescu {
61b2b3212bSRazvan Stefanescu 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
62b2b3212bSRazvan Stefanescu 	struct dpsw_link_state state = {0};
633c7f44faSVladimir Oltean 	int err;
64b2b3212bSRazvan Stefanescu 
653c7f44faSVladimir Oltean 	mutex_lock(&port_priv->mac_lock);
663c7f44faSVladimir Oltean 
673c7f44faSVladimir Oltean 	if (dpaa2_switch_port_is_type_phy(port_priv)) {
683c7f44faSVladimir Oltean 		err = phylink_ethtool_ksettings_get(port_priv->mac->phylink,
6984cba729SIoana Ciornei 						    link_ksettings);
703c7f44faSVladimir Oltean 		mutex_unlock(&port_priv->mac_lock);
713c7f44faSVladimir Oltean 		return err;
723c7f44faSVladimir Oltean 	}
733c7f44faSVladimir Oltean 
743c7f44faSVladimir Oltean 	mutex_unlock(&port_priv->mac_lock);
7584cba729SIoana Ciornei 
76b2b3212bSRazvan Stefanescu 	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
77b2b3212bSRazvan Stefanescu 				     port_priv->ethsw_data->dpsw_handle,
78b2b3212bSRazvan Stefanescu 				     port_priv->idx,
79b2b3212bSRazvan Stefanescu 				     &state);
80b2b3212bSRazvan Stefanescu 	if (err) {
8173866324SIoana Ciornei 		netdev_err(netdev, "ERROR %d getting link state\n", err);
82b2b3212bSRazvan Stefanescu 		goto out;
83b2b3212bSRazvan Stefanescu 	}
84b2b3212bSRazvan Stefanescu 
85b2b3212bSRazvan Stefanescu 	/* At the moment, we have no way of interrogating the DPMAC
86b2b3212bSRazvan Stefanescu 	 * from the DPSW side or there may not exist a DPMAC at all.
87b2b3212bSRazvan Stefanescu 	 * Report only autoneg state, duplexity and speed.
88b2b3212bSRazvan Stefanescu 	 */
89b2b3212bSRazvan Stefanescu 	if (state.options & DPSW_LINK_OPT_AUTONEG)
90b2b3212bSRazvan Stefanescu 		link_ksettings->base.autoneg = AUTONEG_ENABLE;
91b2b3212bSRazvan Stefanescu 	if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
92b2b3212bSRazvan Stefanescu 		link_ksettings->base.duplex = DUPLEX_FULL;
93b2b3212bSRazvan Stefanescu 	link_ksettings->base.speed = state.rate;
94b2b3212bSRazvan Stefanescu 
95b2b3212bSRazvan Stefanescu out:
96b2b3212bSRazvan Stefanescu 	return err;
97b2b3212bSRazvan Stefanescu }
98b2b3212bSRazvan Stefanescu 
99b2b3212bSRazvan Stefanescu static int
dpaa2_switch_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * link_ksettings)1005ad71958SIoana Ciornei dpaa2_switch_set_link_ksettings(struct net_device *netdev,
101b2b3212bSRazvan Stefanescu 				const struct ethtool_link_ksettings *link_ksettings)
102b2b3212bSRazvan Stefanescu {
103b2b3212bSRazvan Stefanescu 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
104c391818aSIoana Ciornei 	struct ethsw_core *ethsw = port_priv->ethsw_data;
105b2b3212bSRazvan Stefanescu 	struct dpsw_link_cfg cfg = {0};
106c391818aSIoana Ciornei 	bool if_running;
107c391818aSIoana Ciornei 	int err = 0, ret;
108b2b3212bSRazvan Stefanescu 
1093c7f44faSVladimir Oltean 	mutex_lock(&port_priv->mac_lock);
1103c7f44faSVladimir Oltean 
1113c7f44faSVladimir Oltean 	if (dpaa2_switch_port_is_type_phy(port_priv)) {
1123c7f44faSVladimir Oltean 		err = phylink_ethtool_ksettings_set(port_priv->mac->phylink,
11384cba729SIoana Ciornei 						    link_ksettings);
1143c7f44faSVladimir Oltean 		mutex_unlock(&port_priv->mac_lock);
1153c7f44faSVladimir Oltean 		return err;
1163c7f44faSVladimir Oltean 	}
1173c7f44faSVladimir Oltean 
1183c7f44faSVladimir Oltean 	mutex_unlock(&port_priv->mac_lock);
11984cba729SIoana Ciornei 
120c391818aSIoana Ciornei 	/* Interface needs to be down to change link settings */
121c391818aSIoana Ciornei 	if_running = netif_running(netdev);
122c391818aSIoana Ciornei 	if (if_running) {
123c391818aSIoana Ciornei 		err = dpsw_if_disable(ethsw->mc_io, 0,
124c391818aSIoana Ciornei 				      ethsw->dpsw_handle,
125c391818aSIoana Ciornei 				      port_priv->idx);
126c391818aSIoana Ciornei 		if (err) {
127c391818aSIoana Ciornei 			netdev_err(netdev, "dpsw_if_disable err %d\n", err);
128c391818aSIoana Ciornei 			return err;
129c391818aSIoana Ciornei 		}
130b2b3212bSRazvan Stefanescu 	}
131b2b3212bSRazvan Stefanescu 
132b2b3212bSRazvan Stefanescu 	cfg.rate = link_ksettings->base.speed;
133b2b3212bSRazvan Stefanescu 	if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
134b2b3212bSRazvan Stefanescu 		cfg.options |= DPSW_LINK_OPT_AUTONEG;
135b2b3212bSRazvan Stefanescu 	else
136b2b3212bSRazvan Stefanescu 		cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
137b2b3212bSRazvan Stefanescu 	if (link_ksettings->base.duplex  == DUPLEX_HALF)
138b2b3212bSRazvan Stefanescu 		cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
139b2b3212bSRazvan Stefanescu 	else
140b2b3212bSRazvan Stefanescu 		cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
141b2b3212bSRazvan Stefanescu 
142b2b3212bSRazvan Stefanescu 	err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
143b2b3212bSRazvan Stefanescu 				   port_priv->ethsw_data->dpsw_handle,
144b2b3212bSRazvan Stefanescu 				   port_priv->idx,
145b2b3212bSRazvan Stefanescu 				   &cfg);
146b2b3212bSRazvan Stefanescu 
147c391818aSIoana Ciornei 	if (if_running) {
148c391818aSIoana Ciornei 		ret = dpsw_if_enable(ethsw->mc_io, 0,
149c391818aSIoana Ciornei 				     ethsw->dpsw_handle,
150c391818aSIoana Ciornei 				     port_priv->idx);
151c391818aSIoana Ciornei 		if (ret) {
152c391818aSIoana Ciornei 			netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
153c391818aSIoana Ciornei 			return ret;
154c391818aSIoana Ciornei 		}
155c391818aSIoana Ciornei 	}
156b2b3212bSRazvan Stefanescu 	return err;
157b2b3212bSRazvan Stefanescu }
158b2b3212bSRazvan Stefanescu 
159f0653a89SIoana Ciornei static int
dpaa2_switch_ethtool_get_sset_count(struct net_device * netdev,int sset)160f0653a89SIoana Ciornei dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
161b2b3212bSRazvan Stefanescu {
162b2b3212bSRazvan Stefanescu 	switch (sset) {
163b2b3212bSRazvan Stefanescu 	case ETH_SS_STATS:
16429811d6eSVladimir Oltean 		return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count();
165b2b3212bSRazvan Stefanescu 	default:
166b2b3212bSRazvan Stefanescu 		return -EOPNOTSUPP;
167b2b3212bSRazvan Stefanescu 	}
168b2b3212bSRazvan Stefanescu }
169b2b3212bSRazvan Stefanescu 
dpaa2_switch_ethtool_get_strings(struct net_device * netdev,u32 stringset,u8 * data)1705ad71958SIoana Ciornei static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
171b2b3212bSRazvan Stefanescu 					     u32 stringset, u8 *data)
172b2b3212bSRazvan Stefanescu {
173*f611cc38SRosen Penev 	const char *str;
174b2b3212bSRazvan Stefanescu 	int i;
175b2b3212bSRazvan Stefanescu 
176b2b3212bSRazvan Stefanescu 	switch (stringset) {
177b2b3212bSRazvan Stefanescu 	case ETH_SS_STATS:
178f0653a89SIoana Ciornei 		for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
179*f611cc38SRosen Penev 			str = dpaa2_switch_ethtool_counters[i].name;
180*f611cc38SRosen Penev 			ethtool_puts(&data, str);
181f0653a89SIoana Ciornei 		}
182*f611cc38SRosen Penev 		dpaa2_mac_get_strings(&data);
183b2b3212bSRazvan Stefanescu 		break;
184b2b3212bSRazvan Stefanescu 	}
185b2b3212bSRazvan Stefanescu }
186b2b3212bSRazvan Stefanescu 
dpaa2_switch_ethtool_get_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)1875ad71958SIoana Ciornei static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
188b2b3212bSRazvan Stefanescu 					   struct ethtool_stats *stats,
189b2b3212bSRazvan Stefanescu 					   u64 *data)
190b2b3212bSRazvan Stefanescu {
191b2b3212bSRazvan Stefanescu 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
192b2b3212bSRazvan Stefanescu 	int i, err;
193b2b3212bSRazvan Stefanescu 
1945ad71958SIoana Ciornei 	for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
195b2b3212bSRazvan Stefanescu 		err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
196b2b3212bSRazvan Stefanescu 					  port_priv->ethsw_data->dpsw_handle,
197b2b3212bSRazvan Stefanescu 					  port_priv->idx,
1985ad71958SIoana Ciornei 					  dpaa2_switch_ethtool_counters[i].id,
199b2b3212bSRazvan Stefanescu 					  &data[i]);
200b2b3212bSRazvan Stefanescu 		if (err)
201b2b3212bSRazvan Stefanescu 			netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
2025ad71958SIoana Ciornei 				   dpaa2_switch_ethtool_counters[i].name, err);
203b2b3212bSRazvan Stefanescu 	}
204f0653a89SIoana Ciornei 
2053c7f44faSVladimir Oltean 	mutex_lock(&port_priv->mac_lock);
2063c7f44faSVladimir Oltean 
207bc230671SVladimir Oltean 	if (dpaa2_switch_port_has_mac(port_priv))
208f0653a89SIoana Ciornei 		dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
2093c7f44faSVladimir Oltean 
2103c7f44faSVladimir Oltean 	mutex_unlock(&port_priv->mac_lock);
211b2b3212bSRazvan Stefanescu }
212b2b3212bSRazvan Stefanescu 
2135ad71958SIoana Ciornei const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
2145ad71958SIoana Ciornei 	.get_drvinfo		= dpaa2_switch_get_drvinfo,
215b2b3212bSRazvan Stefanescu 	.get_link		= ethtool_op_get_link,
2165ad71958SIoana Ciornei 	.get_link_ksettings	= dpaa2_switch_get_link_ksettings,
2175ad71958SIoana Ciornei 	.set_link_ksettings	= dpaa2_switch_set_link_ksettings,
2185ad71958SIoana Ciornei 	.get_strings		= dpaa2_switch_ethtool_get_strings,
2195ad71958SIoana Ciornei 	.get_ethtool_stats	= dpaa2_switch_ethtool_get_stats,
2205ad71958SIoana Ciornei 	.get_sset_count		= dpaa2_switch_ethtool_get_sset_count,
221b2b3212bSRazvan Stefanescu };
222