1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * DPAA2 Ethernet Switch ethtool support
4  *
5  * Copyright 2014-2016 Freescale Semiconductor Inc.
6  * Copyright 2017-2018 NXP
7  *
8  */
9 
10 #include "ethsw.h"
11 
12 static struct {
13 	enum dpsw_counter id;
14 	char name[ETH_GSTRING_LEN];
15 } ethsw_ethtool_counters[] =  {
16 	{DPSW_CNT_ING_FRAME,		"rx frames"},
17 	{DPSW_CNT_ING_BYTE,		"rx bytes"},
18 	{DPSW_CNT_ING_FLTR_FRAME,	"rx filtered frames"},
19 	{DPSW_CNT_ING_FRAME_DISCARD,	"rx discarded frames"},
20 	{DPSW_CNT_ING_BCAST_FRAME,	"rx b-cast frames"},
21 	{DPSW_CNT_ING_BCAST_BYTES,	"rx b-cast bytes"},
22 	{DPSW_CNT_ING_MCAST_FRAME,	"rx m-cast frames"},
23 	{DPSW_CNT_ING_MCAST_BYTE,	"rx m-cast bytes"},
24 	{DPSW_CNT_EGR_FRAME,		"tx frames"},
25 	{DPSW_CNT_EGR_BYTE,		"tx bytes"},
26 	{DPSW_CNT_EGR_FRAME_DISCARD,	"tx discarded frames"},
27 
28 };
29 
30 #define ETHSW_NUM_COUNTERS	ARRAY_SIZE(ethsw_ethtool_counters)
31 
ethsw_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * drvinfo)32 static void ethsw_get_drvinfo(struct net_device *netdev,
33 			      struct ethtool_drvinfo *drvinfo)
34 {
35 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
36 	u16 version_major, version_minor;
37 	int err;
38 
39 	strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
40 
41 	err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
42 				   &version_major,
43 				   &version_minor);
44 	if (err)
45 		strlcpy(drvinfo->fw_version, "N/A",
46 			sizeof(drvinfo->fw_version));
47 	else
48 		snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
49 			 "%u.%u", version_major, version_minor);
50 
51 	strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
52 		sizeof(drvinfo->bus_info));
53 }
54 
55 static int
ethsw_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * link_ksettings)56 ethsw_get_link_ksettings(struct net_device *netdev,
57 			 struct ethtool_link_ksettings *link_ksettings)
58 {
59 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
60 	struct dpsw_link_state state = {0};
61 	int err = 0;
62 
63 	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
64 				     port_priv->ethsw_data->dpsw_handle,
65 				     port_priv->idx,
66 				     &state);
67 	if (err) {
68 		netdev_err(netdev, "ERROR %d getting link state", err);
69 		goto out;
70 	}
71 
72 	/* At the moment, we have no way of interrogating the DPMAC
73 	 * from the DPSW side or there may not exist a DPMAC at all.
74 	 * Report only autoneg state, duplexity and speed.
75 	 */
76 	if (state.options & DPSW_LINK_OPT_AUTONEG)
77 		link_ksettings->base.autoneg = AUTONEG_ENABLE;
78 	if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
79 		link_ksettings->base.duplex = DUPLEX_FULL;
80 	link_ksettings->base.speed = state.rate;
81 
82 out:
83 	return err;
84 }
85 
86 static int
ethsw_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * link_ksettings)87 ethsw_set_link_ksettings(struct net_device *netdev,
88 			 const struct ethtool_link_ksettings *link_ksettings)
89 {
90 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
91 	struct dpsw_link_cfg cfg = {0};
92 	int err = 0;
93 
94 	netdev_dbg(netdev, "Setting link parameters...");
95 
96 	/* Due to a temporary MC limitation, the DPSW port must be down
97 	 * in order to be able to change link settings. Taking steps to let
98 	 * the user know that.
99 	 */
100 	if (netif_running(netdev)) {
101 		netdev_info(netdev, "Sorry, interface must be brought down first.\n");
102 		return -EACCES;
103 	}
104 
105 	cfg.rate = link_ksettings->base.speed;
106 	if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
107 		cfg.options |= DPSW_LINK_OPT_AUTONEG;
108 	else
109 		cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
110 	if (link_ksettings->base.duplex  == DUPLEX_HALF)
111 		cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
112 	else
113 		cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
114 
115 	err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
116 				   port_priv->ethsw_data->dpsw_handle,
117 				   port_priv->idx,
118 				   &cfg);
119 	if (err)
120 		/* ethtool will be loud enough if we return an error; no point
121 		 * in putting our own error message on the console by default
122 		 */
123 		netdev_dbg(netdev, "ERROR %d setting link cfg", err);
124 
125 	return err;
126 }
127 
ethsw_ethtool_get_sset_count(struct net_device * dev,int sset)128 static int ethsw_ethtool_get_sset_count(struct net_device *dev, int sset)
129 {
130 	switch (sset) {
131 	case ETH_SS_STATS:
132 		return ETHSW_NUM_COUNTERS;
133 	default:
134 		return -EOPNOTSUPP;
135 	}
136 }
137 
ethsw_ethtool_get_strings(struct net_device * netdev,u32 stringset,u8 * data)138 static void ethsw_ethtool_get_strings(struct net_device *netdev,
139 				      u32 stringset, u8 *data)
140 {
141 	int i;
142 
143 	switch (stringset) {
144 	case ETH_SS_STATS:
145 		for (i = 0; i < ETHSW_NUM_COUNTERS; i++)
146 			memcpy(data + i * ETH_GSTRING_LEN,
147 			       ethsw_ethtool_counters[i].name, ETH_GSTRING_LEN);
148 		break;
149 	}
150 }
151 
ethsw_ethtool_get_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)152 static void ethsw_ethtool_get_stats(struct net_device *netdev,
153 				    struct ethtool_stats *stats,
154 				    u64 *data)
155 {
156 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
157 	int i, err;
158 
159 	memset(data, 0,
160 	       sizeof(u64) * ETHSW_NUM_COUNTERS);
161 
162 	for (i = 0; i < ETHSW_NUM_COUNTERS; i++) {
163 		err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
164 					  port_priv->ethsw_data->dpsw_handle,
165 					  port_priv->idx,
166 					  ethsw_ethtool_counters[i].id,
167 					  &data[i]);
168 		if (err)
169 			netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
170 				   ethsw_ethtool_counters[i].name, err);
171 	}
172 }
173 
174 const struct ethtool_ops ethsw_port_ethtool_ops = {
175 	.get_drvinfo		= ethsw_get_drvinfo,
176 	.get_link		= ethtool_op_get_link,
177 	.get_link_ksettings	= ethsw_get_link_ksettings,
178 	.set_link_ksettings	= ethsw_set_link_ksettings,
179 	.get_strings		= ethsw_ethtool_get_strings,
180 	.get_ethtool_stats	= ethsw_ethtool_get_stats,
181 	.get_sset_count		= ethsw_ethtool_get_sset_count,
182 };
183