18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  eHEA ethernet device driver for IBM eServer System p
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  (C) Copyright IBM Corp. 2006
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Authors:
108c2ecf20Sopenharmony_ci *       Christoph Raisch <raisch@de.ibm.com>
118c2ecf20Sopenharmony_ci *       Jan-Bernd Themann <themann@de.ibm.com>
128c2ecf20Sopenharmony_ci *       Thomas Klein <tklein@de.ibm.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "ehea.h"
188c2ecf20Sopenharmony_ci#include "ehea_phyp.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic int ehea_get_link_ksettings(struct net_device *dev,
218c2ecf20Sopenharmony_ci				   struct ethtool_link_ksettings *cmd)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
248c2ecf20Sopenharmony_ci	u32 supported, advertising;
258c2ecf20Sopenharmony_ci	u32 speed;
268c2ecf20Sopenharmony_ci	int ret;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	ret = ehea_sense_port_attr(port);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (ret)
318c2ecf20Sopenharmony_ci		return ret;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (netif_carrier_ok(dev)) {
348c2ecf20Sopenharmony_ci		switch (port->port_speed) {
358c2ecf20Sopenharmony_ci		case EHEA_SPEED_10M:
368c2ecf20Sopenharmony_ci			speed = SPEED_10;
378c2ecf20Sopenharmony_ci			break;
388c2ecf20Sopenharmony_ci		case EHEA_SPEED_100M:
398c2ecf20Sopenharmony_ci			speed = SPEED_100;
408c2ecf20Sopenharmony_ci			break;
418c2ecf20Sopenharmony_ci		case EHEA_SPEED_1G:
428c2ecf20Sopenharmony_ci			speed = SPEED_1000;
438c2ecf20Sopenharmony_ci			break;
448c2ecf20Sopenharmony_ci		case EHEA_SPEED_10G:
458c2ecf20Sopenharmony_ci			speed = SPEED_10000;
468c2ecf20Sopenharmony_ci			break;
478c2ecf20Sopenharmony_ci		default:
488c2ecf20Sopenharmony_ci			speed = -1;
498c2ecf20Sopenharmony_ci			break; /* BUG */
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci		cmd->base.duplex = port->full_duplex == 1 ?
528c2ecf20Sopenharmony_ci						     DUPLEX_FULL : DUPLEX_HALF;
538c2ecf20Sopenharmony_ci	} else {
548c2ecf20Sopenharmony_ci		speed = SPEED_UNKNOWN;
558c2ecf20Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	cmd->base.speed = speed;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (cmd->base.speed == SPEED_10000) {
608c2ecf20Sopenharmony_ci		supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
618c2ecf20Sopenharmony_ci		advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE);
628c2ecf20Sopenharmony_ci		cmd->base.port = PORT_FIBRE;
638c2ecf20Sopenharmony_ci	} else {
648c2ecf20Sopenharmony_ci		supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full
658c2ecf20Sopenharmony_ci			       | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full
668c2ecf20Sopenharmony_ci			       | SUPPORTED_10baseT_Half | SUPPORTED_Autoneg
678c2ecf20Sopenharmony_ci			       | SUPPORTED_TP);
688c2ecf20Sopenharmony_ci		advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg
698c2ecf20Sopenharmony_ci				 | ADVERTISED_TP);
708c2ecf20Sopenharmony_ci		cmd->base.port = PORT_TP;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	cmd->base.autoneg = port->autoneg == 1 ?
748c2ecf20Sopenharmony_ci		AUTONEG_ENABLE : AUTONEG_DISABLE;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
778c2ecf20Sopenharmony_ci						supported);
788c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
798c2ecf20Sopenharmony_ci						advertising);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int ehea_set_link_ksettings(struct net_device *dev,
858c2ecf20Sopenharmony_ci				   const struct ethtool_link_ksettings *cmd)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
888c2ecf20Sopenharmony_ci	int ret = 0;
898c2ecf20Sopenharmony_ci	u32 sp;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
928c2ecf20Sopenharmony_ci		sp = EHEA_SPEED_AUTONEG;
938c2ecf20Sopenharmony_ci		goto doit;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	switch (cmd->base.speed) {
978c2ecf20Sopenharmony_ci	case SPEED_10:
988c2ecf20Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_FULL)
998c2ecf20Sopenharmony_ci			sp = H_SPEED_10M_F;
1008c2ecf20Sopenharmony_ci		else
1018c2ecf20Sopenharmony_ci			sp = H_SPEED_10M_H;
1028c2ecf20Sopenharmony_ci		break;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	case SPEED_100:
1058c2ecf20Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_FULL)
1068c2ecf20Sopenharmony_ci			sp = H_SPEED_100M_F;
1078c2ecf20Sopenharmony_ci		else
1088c2ecf20Sopenharmony_ci			sp = H_SPEED_100M_H;
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	case SPEED_1000:
1128c2ecf20Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_FULL)
1138c2ecf20Sopenharmony_ci			sp = H_SPEED_1G_F;
1148c2ecf20Sopenharmony_ci		else
1158c2ecf20Sopenharmony_ci			ret = -EINVAL;
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	case SPEED_10000:
1198c2ecf20Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_FULL)
1208c2ecf20Sopenharmony_ci			sp = H_SPEED_10G_F;
1218c2ecf20Sopenharmony_ci		else
1228c2ecf20Sopenharmony_ci			ret = -EINVAL;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	default:
1268c2ecf20Sopenharmony_ci			ret = -EINVAL;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (ret)
1318c2ecf20Sopenharmony_ci		goto out;
1328c2ecf20Sopenharmony_cidoit:
1338c2ecf20Sopenharmony_ci	ret = ehea_set_portspeed(port, sp);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!ret)
1368c2ecf20Sopenharmony_ci		netdev_info(dev,
1378c2ecf20Sopenharmony_ci			    "Port speed successfully set: %dMbps %s Duplex\n",
1388c2ecf20Sopenharmony_ci			    port->port_speed,
1398c2ecf20Sopenharmony_ci			    port->full_duplex == 1 ? "Full" : "Half");
1408c2ecf20Sopenharmony_ciout:
1418c2ecf20Sopenharmony_ci	return ret;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int ehea_nway_reset(struct net_device *dev)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
1478c2ecf20Sopenharmony_ci	int ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!ret)
1528c2ecf20Sopenharmony_ci		netdev_info(port->netdev,
1538c2ecf20Sopenharmony_ci			    "Port speed successfully set: %dMbps %s Duplex\n",
1548c2ecf20Sopenharmony_ci			    port->port_speed,
1558c2ecf20Sopenharmony_ci			    port->full_duplex == 1 ? "Full" : "Half");
1568c2ecf20Sopenharmony_ci	return ret;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void ehea_get_drvinfo(struct net_device *dev,
1608c2ecf20Sopenharmony_ci			       struct ethtool_drvinfo *info)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
1638c2ecf20Sopenharmony_ci	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic u32 ehea_get_msglevel(struct net_device *dev)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
1698c2ecf20Sopenharmony_ci	return port->msg_enable;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void ehea_set_msglevel(struct net_device *dev, u32 value)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
1758c2ecf20Sopenharmony_ci	port->msg_enable = value;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic const char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
1798c2ecf20Sopenharmony_ci	{"sig_comp_iv"},
1808c2ecf20Sopenharmony_ci	{"swqe_refill_th"},
1818c2ecf20Sopenharmony_ci	{"port resets"},
1828c2ecf20Sopenharmony_ci	{"Receive errors"},
1838c2ecf20Sopenharmony_ci	{"TCP cksum errors"},
1848c2ecf20Sopenharmony_ci	{"IP cksum errors"},
1858c2ecf20Sopenharmony_ci	{"Frame cksum errors"},
1868c2ecf20Sopenharmony_ci	{"num SQ stopped"},
1878c2ecf20Sopenharmony_ci	{"PR0 free_swqes"},
1888c2ecf20Sopenharmony_ci	{"PR1 free_swqes"},
1898c2ecf20Sopenharmony_ci	{"PR2 free_swqes"},
1908c2ecf20Sopenharmony_ci	{"PR3 free_swqes"},
1918c2ecf20Sopenharmony_ci	{"PR4 free_swqes"},
1928c2ecf20Sopenharmony_ci	{"PR5 free_swqes"},
1938c2ecf20Sopenharmony_ci	{"PR6 free_swqes"},
1948c2ecf20Sopenharmony_ci	{"PR7 free_swqes"},
1958c2ecf20Sopenharmony_ci	{"PR8 free_swqes"},
1968c2ecf20Sopenharmony_ci	{"PR9 free_swqes"},
1978c2ecf20Sopenharmony_ci	{"PR10 free_swqes"},
1988c2ecf20Sopenharmony_ci	{"PR11 free_swqes"},
1998c2ecf20Sopenharmony_ci	{"PR12 free_swqes"},
2008c2ecf20Sopenharmony_ci	{"PR13 free_swqes"},
2018c2ecf20Sopenharmony_ci	{"PR14 free_swqes"},
2028c2ecf20Sopenharmony_ci	{"PR15 free_swqes"},
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (stringset == ETH_SS_STATS) {
2088c2ecf20Sopenharmony_ci		memcpy(data, &ehea_ethtool_stats_keys,
2098c2ecf20Sopenharmony_ci		       sizeof(ehea_ethtool_stats_keys));
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int ehea_get_sset_count(struct net_device *dev, int sset)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	switch (sset) {
2168c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
2178c2ecf20Sopenharmony_ci		return ARRAY_SIZE(ehea_ethtool_stats_keys);
2188c2ecf20Sopenharmony_ci	default:
2198c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void ehea_get_ethtool_stats(struct net_device *dev,
2248c2ecf20Sopenharmony_ci				     struct ethtool_stats *stats, u64 *data)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	int i, k, tmp;
2278c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	for (i = 0; i < ehea_get_sset_count(dev, ETH_SS_STATS); i++)
2308c2ecf20Sopenharmony_ci		data[i] = 0;
2318c2ecf20Sopenharmony_ci	i = 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	data[i++] = port->sig_comp_iv;
2348c2ecf20Sopenharmony_ci	data[i++] = port->port_res[0].swqe_refill_th;
2358c2ecf20Sopenharmony_ci	data[i++] = port->resets;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
2388c2ecf20Sopenharmony_ci		tmp += port->port_res[k].p_stats.poll_receive_errors;
2398c2ecf20Sopenharmony_ci	data[i++] = tmp;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
2428c2ecf20Sopenharmony_ci		tmp += port->port_res[k].p_stats.err_tcp_cksum;
2438c2ecf20Sopenharmony_ci	data[i++] = tmp;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
2468c2ecf20Sopenharmony_ci		tmp += port->port_res[k].p_stats.err_ip_cksum;
2478c2ecf20Sopenharmony_ci	data[i++] = tmp;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
2508c2ecf20Sopenharmony_ci		tmp += port->port_res[k].p_stats.err_frame_crc;
2518c2ecf20Sopenharmony_ci	data[i++] = tmp;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
2548c2ecf20Sopenharmony_ci		tmp += port->port_res[k].p_stats.queue_stopped;
2558c2ecf20Sopenharmony_ci	data[i++] = tmp;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	for (k = 0; k < 16; k++)
2588c2ecf20Sopenharmony_ci		data[i++] = atomic_read(&port->port_res[k].swqe_avail);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic const struct ethtool_ops ehea_ethtool_ops = {
2628c2ecf20Sopenharmony_ci	.get_drvinfo = ehea_get_drvinfo,
2638c2ecf20Sopenharmony_ci	.get_msglevel = ehea_get_msglevel,
2648c2ecf20Sopenharmony_ci	.set_msglevel = ehea_set_msglevel,
2658c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
2668c2ecf20Sopenharmony_ci	.get_strings = ehea_get_strings,
2678c2ecf20Sopenharmony_ci	.get_sset_count = ehea_get_sset_count,
2688c2ecf20Sopenharmony_ci	.get_ethtool_stats = ehea_get_ethtool_stats,
2698c2ecf20Sopenharmony_ci	.nway_reset = ehea_nway_reset,		/* Restart autonegotiation */
2708c2ecf20Sopenharmony_ci	.get_link_ksettings = ehea_get_link_ksettings,
2718c2ecf20Sopenharmony_ci	.set_link_ksettings = ehea_set_link_ksettings,
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_civoid ehea_set_ethtool_ops(struct net_device *netdev)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	netdev->ethtool_ops = &ehea_ethtool_ops;
2778c2ecf20Sopenharmony_ci}
278