162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * eHEA ethernet device driver for IBM eServer System p 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2006 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: 1062306a36Sopenharmony_ci * Christoph Raisch <raisch@de.ibm.com> 1162306a36Sopenharmony_ci * Jan-Bernd Themann <themann@de.ibm.com> 1262306a36Sopenharmony_ci * Thomas Klein <tklein@de.ibm.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "ehea.h" 1862306a36Sopenharmony_ci#include "ehea_phyp.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int ehea_get_link_ksettings(struct net_device *dev, 2162306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 2462306a36Sopenharmony_ci u32 supported, advertising; 2562306a36Sopenharmony_ci u32 speed; 2662306a36Sopenharmony_ci int ret; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci ret = ehea_sense_port_attr(port); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (ret) 3162306a36Sopenharmony_ci return ret; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 3462306a36Sopenharmony_ci switch (port->port_speed) { 3562306a36Sopenharmony_ci case EHEA_SPEED_10M: 3662306a36Sopenharmony_ci speed = SPEED_10; 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci case EHEA_SPEED_100M: 3962306a36Sopenharmony_ci speed = SPEED_100; 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case EHEA_SPEED_1G: 4262306a36Sopenharmony_ci speed = SPEED_1000; 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case EHEA_SPEED_10G: 4562306a36Sopenharmony_ci speed = SPEED_10000; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci default: 4862306a36Sopenharmony_ci speed = -1; 4962306a36Sopenharmony_ci break; /* BUG */ 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci cmd->base.duplex = port->full_duplex == 1 ? 5262306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 5362306a36Sopenharmony_ci } else { 5462306a36Sopenharmony_ci speed = SPEED_UNKNOWN; 5562306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci cmd->base.speed = speed; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (cmd->base.speed == SPEED_10000) { 6062306a36Sopenharmony_ci supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); 6162306a36Sopenharmony_ci advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); 6262306a36Sopenharmony_ci cmd->base.port = PORT_FIBRE; 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full 6562306a36Sopenharmony_ci | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full 6662306a36Sopenharmony_ci | SUPPORTED_10baseT_Half | SUPPORTED_Autoneg 6762306a36Sopenharmony_ci | SUPPORTED_TP); 6862306a36Sopenharmony_ci advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg 6962306a36Sopenharmony_ci | ADVERTISED_TP); 7062306a36Sopenharmony_ci cmd->base.port = PORT_TP; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci cmd->base.autoneg = port->autoneg == 1 ? 7462306a36Sopenharmony_ci AUTONEG_ENABLE : AUTONEG_DISABLE; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 7762306a36Sopenharmony_ci supported); 7862306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 7962306a36Sopenharmony_ci advertising); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int ehea_set_link_ksettings(struct net_device *dev, 8562306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 8862306a36Sopenharmony_ci int ret = 0; 8962306a36Sopenharmony_ci u32 sp; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_ENABLE) { 9262306a36Sopenharmony_ci sp = EHEA_SPEED_AUTONEG; 9362306a36Sopenharmony_ci goto doit; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci switch (cmd->base.speed) { 9762306a36Sopenharmony_ci case SPEED_10: 9862306a36Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 9962306a36Sopenharmony_ci sp = H_SPEED_10M_F; 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci sp = H_SPEED_10M_H; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci case SPEED_100: 10562306a36Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 10662306a36Sopenharmony_ci sp = H_SPEED_100M_F; 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci sp = H_SPEED_100M_H; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci case SPEED_1000: 11262306a36Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 11362306a36Sopenharmony_ci sp = H_SPEED_1G_F; 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci ret = -EINVAL; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci case SPEED_10000: 11962306a36Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 12062306a36Sopenharmony_ci sp = H_SPEED_10G_F; 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci ret = -EINVAL; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci ret = -EINVAL; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci goto out; 13262306a36Sopenharmony_cidoit: 13362306a36Sopenharmony_ci ret = ehea_set_portspeed(port, sp); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!ret) 13662306a36Sopenharmony_ci netdev_info(dev, 13762306a36Sopenharmony_ci "Port speed successfully set: %dMbps %s Duplex\n", 13862306a36Sopenharmony_ci port->port_speed, 13962306a36Sopenharmony_ci port->full_duplex == 1 ? "Full" : "Half"); 14062306a36Sopenharmony_ciout: 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int ehea_nway_reset(struct net_device *dev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!ret) 15262306a36Sopenharmony_ci netdev_info(port->netdev, 15362306a36Sopenharmony_ci "Port speed successfully set: %dMbps %s Duplex\n", 15462306a36Sopenharmony_ci port->port_speed, 15562306a36Sopenharmony_ci port->full_duplex == 1 ? "Full" : "Half"); 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void ehea_get_drvinfo(struct net_device *dev, 16062306a36Sopenharmony_ci struct ethtool_drvinfo *info) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 16362306a36Sopenharmony_ci strscpy(info->version, DRV_VERSION, sizeof(info->version)); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic u32 ehea_get_msglevel(struct net_device *dev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 16962306a36Sopenharmony_ci return port->msg_enable; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void ehea_set_msglevel(struct net_device *dev, u32 value) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 17562306a36Sopenharmony_ci port->msg_enable = value; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = { 17962306a36Sopenharmony_ci {"sig_comp_iv"}, 18062306a36Sopenharmony_ci {"swqe_refill_th"}, 18162306a36Sopenharmony_ci {"port resets"}, 18262306a36Sopenharmony_ci {"Receive errors"}, 18362306a36Sopenharmony_ci {"TCP cksum errors"}, 18462306a36Sopenharmony_ci {"IP cksum errors"}, 18562306a36Sopenharmony_ci {"Frame cksum errors"}, 18662306a36Sopenharmony_ci {"num SQ stopped"}, 18762306a36Sopenharmony_ci {"PR0 free_swqes"}, 18862306a36Sopenharmony_ci {"PR1 free_swqes"}, 18962306a36Sopenharmony_ci {"PR2 free_swqes"}, 19062306a36Sopenharmony_ci {"PR3 free_swqes"}, 19162306a36Sopenharmony_ci {"PR4 free_swqes"}, 19262306a36Sopenharmony_ci {"PR5 free_swqes"}, 19362306a36Sopenharmony_ci {"PR6 free_swqes"}, 19462306a36Sopenharmony_ci {"PR7 free_swqes"}, 19562306a36Sopenharmony_ci {"PR8 free_swqes"}, 19662306a36Sopenharmony_ci {"PR9 free_swqes"}, 19762306a36Sopenharmony_ci {"PR10 free_swqes"}, 19862306a36Sopenharmony_ci {"PR11 free_swqes"}, 19962306a36Sopenharmony_ci {"PR12 free_swqes"}, 20062306a36Sopenharmony_ci {"PR13 free_swqes"}, 20162306a36Sopenharmony_ci {"PR14 free_swqes"}, 20262306a36Sopenharmony_ci {"PR15 free_swqes"}, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) { 20862306a36Sopenharmony_ci memcpy(data, &ehea_ethtool_stats_keys, 20962306a36Sopenharmony_ci sizeof(ehea_ethtool_stats_keys)); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int ehea_get_sset_count(struct net_device *dev, int sset) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci switch (sset) { 21662306a36Sopenharmony_ci case ETH_SS_STATS: 21762306a36Sopenharmony_ci return ARRAY_SIZE(ehea_ethtool_stats_keys); 21862306a36Sopenharmony_ci default: 21962306a36Sopenharmony_ci return -EOPNOTSUPP; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void ehea_get_ethtool_stats(struct net_device *dev, 22462306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int i, k, tmp; 22762306a36Sopenharmony_ci struct ehea_port *port = netdev_priv(dev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for (i = 0; i < ehea_get_sset_count(dev, ETH_SS_STATS); i++) 23062306a36Sopenharmony_ci data[i] = 0; 23162306a36Sopenharmony_ci i = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci data[i++] = port->sig_comp_iv; 23462306a36Sopenharmony_ci data[i++] = port->port_res[0].swqe_refill_th; 23562306a36Sopenharmony_ci data[i++] = port->resets; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) 23862306a36Sopenharmony_ci tmp += port->port_res[k].p_stats.poll_receive_errors; 23962306a36Sopenharmony_ci data[i++] = tmp; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) 24262306a36Sopenharmony_ci tmp += port->port_res[k].p_stats.err_tcp_cksum; 24362306a36Sopenharmony_ci data[i++] = tmp; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) 24662306a36Sopenharmony_ci tmp += port->port_res[k].p_stats.err_ip_cksum; 24762306a36Sopenharmony_ci data[i++] = tmp; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) 25062306a36Sopenharmony_ci tmp += port->port_res[k].p_stats.err_frame_crc; 25162306a36Sopenharmony_ci data[i++] = tmp; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) 25462306a36Sopenharmony_ci tmp += port->port_res[k].p_stats.queue_stopped; 25562306a36Sopenharmony_ci data[i++] = tmp; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (k = 0; k < 16; k++) 25862306a36Sopenharmony_ci data[i++] = atomic_read(&port->port_res[k].swqe_avail); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic const struct ethtool_ops ehea_ethtool_ops = { 26262306a36Sopenharmony_ci .get_drvinfo = ehea_get_drvinfo, 26362306a36Sopenharmony_ci .get_msglevel = ehea_get_msglevel, 26462306a36Sopenharmony_ci .set_msglevel = ehea_set_msglevel, 26562306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 26662306a36Sopenharmony_ci .get_strings = ehea_get_strings, 26762306a36Sopenharmony_ci .get_sset_count = ehea_get_sset_count, 26862306a36Sopenharmony_ci .get_ethtool_stats = ehea_get_ethtool_stats, 26962306a36Sopenharmony_ci .nway_reset = ehea_nway_reset, /* Restart autonegotiation */ 27062306a36Sopenharmony_ci .get_link_ksettings = ehea_get_link_ksettings, 27162306a36Sopenharmony_ci .set_link_ksettings = ehea_set_link_ksettings, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid ehea_set_ethtool_ops(struct net_device *netdev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci netdev->ethtool_ops = &ehea_ethtool_ops; 27762306a36Sopenharmony_ci} 278