1 /*
2  *  linux/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
3  *
4  *  eHEA ethernet device driver for IBM eServer System p
5  *
6  *  (C) Copyright IBM Corp. 2006
7  *
8  *  Authors:
9  *       Christoph Raisch <raisch@de.ibm.com>
10  *       Jan-Bernd Themann <themann@de.ibm.com>
11  *       Thomas Klein <tklein@de.ibm.com>
12  *
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  */
28 
29 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30 
31 #include "ehea.h"
32 #include "ehea_phyp.h"
33 
34 static int ehea_get_link_ksettings(struct net_device *dev,
35 				   struct ethtool_link_ksettings *cmd)
36 {
37 	struct ehea_port *port = netdev_priv(dev);
38 	u32 supported, advertising;
39 	u32 speed;
40 	int ret;
41 
42 	ret = ehea_sense_port_attr(port);
43 
44 	if (ret)
45 		return ret;
46 
47 	if (netif_carrier_ok(dev)) {
48 		switch (port->port_speed) {
49 		case EHEA_SPEED_10M:
50 			speed = SPEED_10;
51 			break;
52 		case EHEA_SPEED_100M:
53 			speed = SPEED_100;
54 			break;
55 		case EHEA_SPEED_1G:
56 			speed = SPEED_1000;
57 			break;
58 		case EHEA_SPEED_10G:
59 			speed = SPEED_10000;
60 			break;
61 		default:
62 			speed = -1;
63 			break; /* BUG */
64 		}
65 		cmd->base.duplex = port->full_duplex == 1 ?
66 						     DUPLEX_FULL : DUPLEX_HALF;
67 	} else {
68 		speed = SPEED_UNKNOWN;
69 		cmd->base.duplex = DUPLEX_UNKNOWN;
70 	}
71 	cmd->base.speed = speed;
72 
73 	if (cmd->base.speed == SPEED_10000) {
74 		supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
75 		advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE);
76 		cmd->base.port = PORT_FIBRE;
77 	} else {
78 		supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full
79 			       | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full
80 			       | SUPPORTED_10baseT_Half | SUPPORTED_Autoneg
81 			       | SUPPORTED_TP);
82 		advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg
83 				 | ADVERTISED_TP);
84 		cmd->base.port = PORT_TP;
85 	}
86 
87 	cmd->base.autoneg = port->autoneg == 1 ?
88 		AUTONEG_ENABLE : AUTONEG_DISABLE;
89 
90 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
91 						supported);
92 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
93 						advertising);
94 
95 	return 0;
96 }
97 
98 static int ehea_set_link_ksettings(struct net_device *dev,
99 				   const struct ethtool_link_ksettings *cmd)
100 {
101 	struct ehea_port *port = netdev_priv(dev);
102 	int ret = 0;
103 	u32 sp;
104 
105 	if (cmd->base.autoneg == AUTONEG_ENABLE) {
106 		sp = EHEA_SPEED_AUTONEG;
107 		goto doit;
108 	}
109 
110 	switch (cmd->base.speed) {
111 	case SPEED_10:
112 		if (cmd->base.duplex == DUPLEX_FULL)
113 			sp = H_SPEED_10M_F;
114 		else
115 			sp = H_SPEED_10M_H;
116 		break;
117 
118 	case SPEED_100:
119 		if (cmd->base.duplex == DUPLEX_FULL)
120 			sp = H_SPEED_100M_F;
121 		else
122 			sp = H_SPEED_100M_H;
123 		break;
124 
125 	case SPEED_1000:
126 		if (cmd->base.duplex == DUPLEX_FULL)
127 			sp = H_SPEED_1G_F;
128 		else
129 			ret = -EINVAL;
130 		break;
131 
132 	case SPEED_10000:
133 		if (cmd->base.duplex == DUPLEX_FULL)
134 			sp = H_SPEED_10G_F;
135 		else
136 			ret = -EINVAL;
137 		break;
138 
139 	default:
140 			ret = -EINVAL;
141 		break;
142 	}
143 
144 	if (ret)
145 		goto out;
146 doit:
147 	ret = ehea_set_portspeed(port, sp);
148 
149 	if (!ret)
150 		netdev_info(dev,
151 			    "Port speed successfully set: %dMbps %s Duplex\n",
152 			    port->port_speed,
153 			    port->full_duplex == 1 ? "Full" : "Half");
154 out:
155 	return ret;
156 }
157 
158 static int ehea_nway_reset(struct net_device *dev)
159 {
160 	struct ehea_port *port = netdev_priv(dev);
161 	int ret;
162 
163 	ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
164 
165 	if (!ret)
166 		netdev_info(port->netdev,
167 			    "Port speed successfully set: %dMbps %s Duplex\n",
168 			    port->port_speed,
169 			    port->full_duplex == 1 ? "Full" : "Half");
170 	return ret;
171 }
172 
173 static void ehea_get_drvinfo(struct net_device *dev,
174 			       struct ethtool_drvinfo *info)
175 {
176 	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
177 	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
178 }
179 
180 static u32 ehea_get_msglevel(struct net_device *dev)
181 {
182 	struct ehea_port *port = netdev_priv(dev);
183 	return port->msg_enable;
184 }
185 
186 static void ehea_set_msglevel(struct net_device *dev, u32 value)
187 {
188 	struct ehea_port *port = netdev_priv(dev);
189 	port->msg_enable = value;
190 }
191 
192 static const char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
193 	{"sig_comp_iv"},
194 	{"swqe_refill_th"},
195 	{"port resets"},
196 	{"Receive errors"},
197 	{"TCP cksum errors"},
198 	{"IP cksum errors"},
199 	{"Frame cksum errors"},
200 	{"num SQ stopped"},
201 	{"PR0 free_swqes"},
202 	{"PR1 free_swqes"},
203 	{"PR2 free_swqes"},
204 	{"PR3 free_swqes"},
205 	{"PR4 free_swqes"},
206 	{"PR5 free_swqes"},
207 	{"PR6 free_swqes"},
208 	{"PR7 free_swqes"},
209 	{"PR8 free_swqes"},
210 	{"PR9 free_swqes"},
211 	{"PR10 free_swqes"},
212 	{"PR11 free_swqes"},
213 	{"PR12 free_swqes"},
214 	{"PR13 free_swqes"},
215 	{"PR14 free_swqes"},
216 	{"PR15 free_swqes"},
217 };
218 
219 static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
220 {
221 	if (stringset == ETH_SS_STATS) {
222 		memcpy(data, &ehea_ethtool_stats_keys,
223 		       sizeof(ehea_ethtool_stats_keys));
224 	}
225 }
226 
227 static int ehea_get_sset_count(struct net_device *dev, int sset)
228 {
229 	switch (sset) {
230 	case ETH_SS_STATS:
231 		return ARRAY_SIZE(ehea_ethtool_stats_keys);
232 	default:
233 		return -EOPNOTSUPP;
234 	}
235 }
236 
237 static void ehea_get_ethtool_stats(struct net_device *dev,
238 				     struct ethtool_stats *stats, u64 *data)
239 {
240 	int i, k, tmp;
241 	struct ehea_port *port = netdev_priv(dev);
242 
243 	for (i = 0; i < ehea_get_sset_count(dev, ETH_SS_STATS); i++)
244 		data[i] = 0;
245 	i = 0;
246 
247 	data[i++] = port->sig_comp_iv;
248 	data[i++] = port->port_res[0].swqe_refill_th;
249 	data[i++] = port->resets;
250 
251 	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
252 		tmp += port->port_res[k].p_stats.poll_receive_errors;
253 	data[i++] = tmp;
254 
255 	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
256 		tmp += port->port_res[k].p_stats.err_tcp_cksum;
257 	data[i++] = tmp;
258 
259 	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
260 		tmp += port->port_res[k].p_stats.err_ip_cksum;
261 	data[i++] = tmp;
262 
263 	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
264 		tmp += port->port_res[k].p_stats.err_frame_crc;
265 	data[i++] = tmp;
266 
267 	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
268 		tmp += port->port_res[k].p_stats.queue_stopped;
269 	data[i++] = tmp;
270 
271 	for (k = 0; k < 16; k++)
272 		data[i++] = atomic_read(&port->port_res[k].swqe_avail);
273 }
274 
275 static const struct ethtool_ops ehea_ethtool_ops = {
276 	.get_drvinfo = ehea_get_drvinfo,
277 	.get_msglevel = ehea_get_msglevel,
278 	.set_msglevel = ehea_set_msglevel,
279 	.get_link = ethtool_op_get_link,
280 	.get_strings = ehea_get_strings,
281 	.get_sset_count = ehea_get_sset_count,
282 	.get_ethtool_stats = ehea_get_ethtool_stats,
283 	.nway_reset = ehea_nway_reset,		/* Restart autonegotiation */
284 	.get_link_ksettings = ehea_get_link_ksettings,
285 	.set_link_ksettings = ehea_set_link_ksettings,
286 };
287 
288 void ehea_set_ethtool_ops(struct net_device *netdev)
289 {
290 	netdev->ethtool_ops = &ehea_ethtool_ops;
291 }
292