18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2018 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "qeth" 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 108c2ecf20Sopenharmony_ci#include "qeth_core.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define QETH_TXQ_STAT(_name, _stat) { \ 148c2ecf20Sopenharmony_ci .name = _name, \ 158c2ecf20Sopenharmony_ci .offset = offsetof(struct qeth_out_q_stats, _stat) \ 168c2ecf20Sopenharmony_ci} 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define QETH_CARD_STAT(_name, _stat) { \ 198c2ecf20Sopenharmony_ci .name = _name, \ 208c2ecf20Sopenharmony_ci .offset = offsetof(struct qeth_card_stats, _stat) \ 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct qeth_stats { 248c2ecf20Sopenharmony_ci char name[ETH_GSTRING_LEN]; 258c2ecf20Sopenharmony_ci unsigned int offset; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct qeth_stats txq_stats[] = { 298c2ecf20Sopenharmony_ci QETH_TXQ_STAT("IO buffers", bufs), 308c2ecf20Sopenharmony_ci QETH_TXQ_STAT("IO buffer elements", buf_elements), 318c2ecf20Sopenharmony_ci QETH_TXQ_STAT("packed IO buffers", bufs_pack), 328c2ecf20Sopenharmony_ci QETH_TXQ_STAT("skbs", tx_packets), 338c2ecf20Sopenharmony_ci QETH_TXQ_STAT("packed skbs", skbs_pack), 348c2ecf20Sopenharmony_ci QETH_TXQ_STAT("SG skbs", skbs_sg), 358c2ecf20Sopenharmony_ci QETH_TXQ_STAT("HW csum skbs", skbs_csum), 368c2ecf20Sopenharmony_ci QETH_TXQ_STAT("TSO skbs", skbs_tso), 378c2ecf20Sopenharmony_ci QETH_TXQ_STAT("linearized skbs", skbs_linearized), 388c2ecf20Sopenharmony_ci QETH_TXQ_STAT("linearized+error skbs", skbs_linearized_fail), 398c2ecf20Sopenharmony_ci QETH_TXQ_STAT("TSO bytes", tso_bytes), 408c2ecf20Sopenharmony_ci QETH_TXQ_STAT("Packing mode switches", packing_mode_switch), 418c2ecf20Sopenharmony_ci QETH_TXQ_STAT("Queue stopped", stopped), 428c2ecf20Sopenharmony_ci QETH_TXQ_STAT("Doorbell", doorbell), 438c2ecf20Sopenharmony_ci QETH_TXQ_STAT("IRQ for frames", coal_frames), 448c2ecf20Sopenharmony_ci QETH_TXQ_STAT("Completion yield", completion_yield), 458c2ecf20Sopenharmony_ci QETH_TXQ_STAT("Completion timer", completion_timer), 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const struct qeth_stats card_stats[] = { 498c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 IO buffers", rx_bufs), 508c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 HW csum skbs", rx_skb_csum), 518c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs), 528c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags), 538c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page), 548c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem), 558c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp), 568c2ecf20Sopenharmony_ci QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt), 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) 608c2ecf20Sopenharmony_ci#define CARD_STATS_LEN ARRAY_SIZE(card_stats) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void qeth_add_stat_data(u64 **dst, void *src, 638c2ecf20Sopenharmony_ci const struct qeth_stats stats[], 648c2ecf20Sopenharmony_ci unsigned int size) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci unsigned int i; 678c2ecf20Sopenharmony_ci char *stat; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 708c2ecf20Sopenharmony_ci stat = (char *)src + stats[i].offset; 718c2ecf20Sopenharmony_ci **dst = *(u64 *)stat; 728c2ecf20Sopenharmony_ci (*dst)++; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void qeth_add_stat_strings(u8 **data, const char *prefix, 778c2ecf20Sopenharmony_ci const struct qeth_stats stats[], 788c2ecf20Sopenharmony_ci unsigned int size) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci unsigned int i; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 838c2ecf20Sopenharmony_ci snprintf(*data, ETH_GSTRING_LEN, "%s%s", prefix, stats[i].name); 848c2ecf20Sopenharmony_ci *data += ETH_GSTRING_LEN; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int qeth_get_sset_count(struct net_device *dev, int stringset) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci switch (stringset) { 938c2ecf20Sopenharmony_ci case ETH_SS_STATS: 948c2ecf20Sopenharmony_ci return CARD_STATS_LEN + 958c2ecf20Sopenharmony_ci card->qdio.no_out_queues * TXQ_STATS_LEN; 968c2ecf20Sopenharmony_ci default: 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void qeth_get_ethtool_stats(struct net_device *dev, 1028c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 1058c2ecf20Sopenharmony_ci unsigned int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci qeth_add_stat_data(&data, &card->stats, card_stats, CARD_STATS_LEN); 1088c2ecf20Sopenharmony_ci for (i = 0; i < card->qdio.no_out_queues; i++) 1098c2ecf20Sopenharmony_ci qeth_add_stat_data(&data, &card->qdio.out_qs[i]->stats, 1108c2ecf20Sopenharmony_ci txq_stats, TXQ_STATS_LEN); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void __qeth_set_coalesce(struct net_device *dev, 1148c2ecf20Sopenharmony_ci struct qeth_qdio_out_q *queue, 1158c2ecf20Sopenharmony_ci struct ethtool_coalesce *coal) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci WRITE_ONCE(queue->coalesce_usecs, coal->tx_coalesce_usecs); 1188c2ecf20Sopenharmony_ci WRITE_ONCE(queue->max_coalesced_frames, coal->tx_max_coalesced_frames); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (coal->tx_coalesce_usecs && 1218c2ecf20Sopenharmony_ci netif_running(dev) && 1228c2ecf20Sopenharmony_ci !qeth_out_queue_is_empty(queue)) 1238c2ecf20Sopenharmony_ci qeth_tx_arm_timer(queue, coal->tx_coalesce_usecs); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int qeth_set_coalesce(struct net_device *dev, 1278c2ecf20Sopenharmony_ci struct ethtool_coalesce *coal) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 1308c2ecf20Sopenharmony_ci struct qeth_qdio_out_q *queue; 1318c2ecf20Sopenharmony_ci unsigned int i; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!IS_IQD(card)) 1348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!coal->tx_coalesce_usecs && !coal->tx_max_coalesced_frames) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci qeth_for_each_output_queue(card, queue, i) 1408c2ecf20Sopenharmony_ci __qeth_set_coalesce(dev, queue, coal); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void qeth_get_ringparam(struct net_device *dev, 1468c2ecf20Sopenharmony_ci struct ethtool_ringparam *param) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci param->rx_max_pending = QDIO_MAX_BUFFERS_PER_Q; 1518c2ecf20Sopenharmony_ci param->rx_mini_max_pending = 0; 1528c2ecf20Sopenharmony_ci param->rx_jumbo_max_pending = 0; 1538c2ecf20Sopenharmony_ci param->tx_max_pending = QDIO_MAX_BUFFERS_PER_Q; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci param->rx_pending = card->qdio.in_buf_pool.buf_count; 1568c2ecf20Sopenharmony_ci param->rx_mini_pending = 0; 1578c2ecf20Sopenharmony_ci param->rx_jumbo_pending = 0; 1588c2ecf20Sopenharmony_ci param->tx_pending = QDIO_MAX_BUFFERS_PER_Q; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void qeth_get_strings(struct net_device *dev, u32 stringset, u8 *data) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 1648c2ecf20Sopenharmony_ci char prefix[ETH_GSTRING_LEN] = ""; 1658c2ecf20Sopenharmony_ci unsigned int i; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (stringset) { 1688c2ecf20Sopenharmony_ci case ETH_SS_STATS: 1698c2ecf20Sopenharmony_ci qeth_add_stat_strings(&data, prefix, card_stats, 1708c2ecf20Sopenharmony_ci CARD_STATS_LEN); 1718c2ecf20Sopenharmony_ci for (i = 0; i < card->qdio.no_out_queues; i++) { 1728c2ecf20Sopenharmony_ci snprintf(prefix, ETH_GSTRING_LEN, "tx%u ", i); 1738c2ecf20Sopenharmony_ci qeth_add_stat_strings(&data, prefix, txq_stats, 1748c2ecf20Sopenharmony_ci TXQ_STATS_LEN); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci default: 1788c2ecf20Sopenharmony_ci WARN_ON(1); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void qeth_get_drvinfo(struct net_device *dev, 1848c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci strlcpy(info->driver, IS_LAYER2(card) ? "qeth_l2" : "qeth_l3", 1898c2ecf20Sopenharmony_ci sizeof(info->driver)); 1908c2ecf20Sopenharmony_ci strlcpy(info->fw_version, card->info.mcl_level, 1918c2ecf20Sopenharmony_ci sizeof(info->fw_version)); 1928c2ecf20Sopenharmony_ci snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", 1938c2ecf20Sopenharmony_ci CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void qeth_get_channels(struct net_device *dev, 1978c2ecf20Sopenharmony_ci struct ethtool_channels *channels) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci channels->max_rx = dev->num_rx_queues; 2028c2ecf20Sopenharmony_ci channels->max_tx = card->qdio.no_out_queues; 2038c2ecf20Sopenharmony_ci channels->max_other = 0; 2048c2ecf20Sopenharmony_ci channels->max_combined = 0; 2058c2ecf20Sopenharmony_ci channels->rx_count = dev->real_num_rx_queues; 2068c2ecf20Sopenharmony_ci channels->tx_count = dev->real_num_tx_queues; 2078c2ecf20Sopenharmony_ci channels->other_count = 0; 2088c2ecf20Sopenharmony_ci channels->combined_count = 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int qeth_set_channels(struct net_device *dev, 2128c2ecf20Sopenharmony_ci struct ethtool_channels *channels) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(dev); 2158c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2168c2ecf20Sopenharmony_ci int rc; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (channels->rx_count == 0 || channels->tx_count == 0) 2198c2ecf20Sopenharmony_ci return -EINVAL; 2208c2ecf20Sopenharmony_ci if (channels->tx_count > card->qdio.no_out_queues) 2218c2ecf20Sopenharmony_ci return -EINVAL; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Prio-queueing needs all TX queues: */ 2248c2ecf20Sopenharmony_ci if (qeth_uses_tx_prio_queueing(card)) 2258c2ecf20Sopenharmony_ci return -EPERM; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (IS_IQD(card)) { 2288c2ecf20Sopenharmony_ci if (channels->tx_count < QETH_IQD_MIN_TXQ) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Reject downgrade while running. It could push displaced 2328c2ecf20Sopenharmony_ci * ucast flows onto txq0, which is reserved for mcast. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci if (netif_running(dev) && 2358c2ecf20Sopenharmony_ci channels->tx_count < dev->real_num_tx_queues) 2368c2ecf20Sopenharmony_ci return -EPERM; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci rc = qeth_set_real_num_tx_queues(card, channels->tx_count); 2408c2ecf20Sopenharmony_ci if (!rc) 2418c2ecf20Sopenharmony_ci priv->tx_wanted_queues = channels->tx_count; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return rc; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int qeth_get_ts_info(struct net_device *dev, 2478c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!IS_IQD(card)) 2528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return ethtool_op_get_ts_info(dev, info); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int qeth_get_tunable(struct net_device *dev, 2588c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, void *data) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(dev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (tuna->id) { 2638c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 2648c2ecf20Sopenharmony_ci *(u32 *)data = priv->rx_copybreak; 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci default: 2678c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int qeth_set_tunable(struct net_device *dev, 2728c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, 2738c2ecf20Sopenharmony_ci const void *data) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct qeth_priv *priv = netdev_priv(dev); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci switch (tuna->id) { 2788c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 2798c2ecf20Sopenharmony_ci WRITE_ONCE(priv->rx_copybreak, *(u32 *)data); 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int qeth_get_per_queue_coalesce(struct net_device *dev, u32 __queue, 2878c2ecf20Sopenharmony_ci struct ethtool_coalesce *coal) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 2908c2ecf20Sopenharmony_ci struct qeth_qdio_out_q *queue; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!IS_IQD(card)) 2938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (__queue >= card->qdio.no_out_queues) 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci queue = card->qdio.out_qs[__queue]; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci coal->tx_coalesce_usecs = queue->coalesce_usecs; 3018c2ecf20Sopenharmony_ci coal->tx_max_coalesced_frames = queue->max_coalesced_frames; 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int qeth_set_per_queue_coalesce(struct net_device *dev, u32 queue, 3068c2ecf20Sopenharmony_ci struct ethtool_coalesce *coal) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!IS_IQD(card)) 3118c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (queue >= card->qdio.no_out_queues) 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!coal->tx_coalesce_usecs && !coal->tx_max_coalesced_frames) 3178c2ecf20Sopenharmony_ci return -EINVAL; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci __qeth_set_coalesce(dev, card->qdio.out_qs[queue], coal); 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* Helper function to fill 'advertising' and 'supported' which are the same. */ 3248c2ecf20Sopenharmony_ci/* Autoneg and full-duplex are supported and advertised unconditionally. */ 3258c2ecf20Sopenharmony_ci/* Always advertise and support all speeds up to specified, and only one */ 3268c2ecf20Sopenharmony_ci/* specified port type. */ 3278c2ecf20Sopenharmony_cistatic void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, 3288c2ecf20Sopenharmony_ci int maxspeed, int porttype) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(cmd, supported); 3318c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(cmd, advertising); 3328c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); 3358c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci switch (porttype) { 3388c2ecf20Sopenharmony_ci case PORT_TP: 3398c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 3408c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci case PORT_FIBRE: 3438c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); 3448c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci default: 3478c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 3488c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 3498c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* partially does fall through, to also select lower speeds */ 3538c2ecf20Sopenharmony_ci switch (maxspeed) { 3548c2ecf20Sopenharmony_ci case SPEED_25000: 3558c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3568c2ecf20Sopenharmony_ci 25000baseSR_Full); 3578c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3588c2ecf20Sopenharmony_ci 25000baseSR_Full); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case SPEED_10000: 3618c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3628c2ecf20Sopenharmony_ci 10000baseT_Full); 3638c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3648c2ecf20Sopenharmony_ci 10000baseT_Full); 3658c2ecf20Sopenharmony_ci fallthrough; 3668c2ecf20Sopenharmony_ci case SPEED_1000: 3678c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3688c2ecf20Sopenharmony_ci 1000baseT_Full); 3698c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3708c2ecf20Sopenharmony_ci 1000baseT_Full); 3718c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3728c2ecf20Sopenharmony_ci 1000baseT_Half); 3738c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3748c2ecf20Sopenharmony_ci 1000baseT_Half); 3758c2ecf20Sopenharmony_ci fallthrough; 3768c2ecf20Sopenharmony_ci case SPEED_100: 3778c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3788c2ecf20Sopenharmony_ci 100baseT_Full); 3798c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3808c2ecf20Sopenharmony_ci 100baseT_Full); 3818c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3828c2ecf20Sopenharmony_ci 100baseT_Half); 3838c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3848c2ecf20Sopenharmony_ci 100baseT_Half); 3858c2ecf20Sopenharmony_ci fallthrough; 3868c2ecf20Sopenharmony_ci case SPEED_10: 3878c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3888c2ecf20Sopenharmony_ci 10baseT_Full); 3898c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3908c2ecf20Sopenharmony_ci 10baseT_Full); 3918c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3928c2ecf20Sopenharmony_ci 10baseT_Half); 3938c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 3948c2ecf20Sopenharmony_ci 10baseT_Half); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 3988c2ecf20Sopenharmony_ci 10baseT_Full); 3998c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 4008c2ecf20Sopenharmony_ci 10baseT_Full); 4018c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, 4028c2ecf20Sopenharmony_ci 10baseT_Half); 4038c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, 4048c2ecf20Sopenharmony_ci 10baseT_Half); 4058c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int qeth_get_link_ksettings(struct net_device *netdev, 4118c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct qeth_card *card = netdev->ml_priv; 4148c2ecf20Sopenharmony_ci enum qeth_link_types link_type; 4158c2ecf20Sopenharmony_ci struct carrier_info carrier_info; 4168c2ecf20Sopenharmony_ci int rc; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (IS_IQD(card) || IS_VM_NIC(card)) 4198c2ecf20Sopenharmony_ci link_type = QETH_LINK_TYPE_10GBIT_ETH; 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci link_type = card->info.link_type; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 4248c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 4258c2ecf20Sopenharmony_ci cmd->base.phy_address = 0; 4268c2ecf20Sopenharmony_ci cmd->base.mdio_support = 0; 4278c2ecf20Sopenharmony_ci cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; 4288c2ecf20Sopenharmony_ci cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci switch (link_type) { 4318c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_FAST_ETH: 4328c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_LANE_ETH100: 4338c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_100; 4348c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_GBIT_ETH: 4378c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_LANE_ETH1000: 4388c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_1000; 4398c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_10GBIT_ETH: 4428c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10000; 4438c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case QETH_LINK_TYPE_25GBIT_ETH: 4468c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_25000; 4478c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci default: 4508c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 4518c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Check if we can obtain more accurate information. */ 4568c2ecf20Sopenharmony_ci /* If QUERY_CARD_INFO command is not supported or fails, */ 4578c2ecf20Sopenharmony_ci /* just return the heuristics that was filled above. */ 4588c2ecf20Sopenharmony_ci rc = qeth_query_card_info(card, &carrier_info); 4598c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */ 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci if (rc) /* report error from the hardware operation */ 4628c2ecf20Sopenharmony_ci return rc; 4638c2ecf20Sopenharmony_ci /* on success, fill in the information got from the hardware */ 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci netdev_dbg(netdev, 4668c2ecf20Sopenharmony_ci "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", 4678c2ecf20Sopenharmony_ci carrier_info.card_type, 4688c2ecf20Sopenharmony_ci carrier_info.port_mode, 4698c2ecf20Sopenharmony_ci carrier_info.port_speed); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Update attributes for which we've obtained more authoritative */ 4728c2ecf20Sopenharmony_ci /* information, leave the rest the way they where filled above. */ 4738c2ecf20Sopenharmony_ci switch (carrier_info.card_type) { 4748c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_1G_COPPER_A: 4758c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_1G_COPPER_B: 4768c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 4778c2ecf20Sopenharmony_ci qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_1G_FIBRE_A: 4808c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_1G_FIBRE_B: 4818c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 4828c2ecf20Sopenharmony_ci qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_10G_FIBRE_A: 4858c2ecf20Sopenharmony_ci case CARD_INFO_TYPE_10G_FIBRE_B: 4868c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 4878c2ecf20Sopenharmony_ci qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port); 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci switch (carrier_info.port_mode) { 4928c2ecf20Sopenharmony_ci case CARD_INFO_PORTM_FULLDUPLEX: 4938c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci case CARD_INFO_PORTM_HALFDUPLEX: 4968c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci switch (carrier_info.port_speed) { 5018c2ecf20Sopenharmony_ci case CARD_INFO_PORTS_10M: 5028c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case CARD_INFO_PORTS_100M: 5058c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_100; 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case CARD_INFO_PORTS_1G: 5088c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_1000; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case CARD_INFO_PORTS_10G: 5118c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10000; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case CARD_INFO_PORTS_25G: 5148c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_25000; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciconst struct ethtool_ops qeth_ethtool_ops = { 5228c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_TX_USECS | 5238c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_TX_MAX_FRAMES, 5248c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 5258c2ecf20Sopenharmony_ci .set_coalesce = qeth_set_coalesce, 5268c2ecf20Sopenharmony_ci .get_ringparam = qeth_get_ringparam, 5278c2ecf20Sopenharmony_ci .get_strings = qeth_get_strings, 5288c2ecf20Sopenharmony_ci .get_ethtool_stats = qeth_get_ethtool_stats, 5298c2ecf20Sopenharmony_ci .get_sset_count = qeth_get_sset_count, 5308c2ecf20Sopenharmony_ci .get_drvinfo = qeth_get_drvinfo, 5318c2ecf20Sopenharmony_ci .get_channels = qeth_get_channels, 5328c2ecf20Sopenharmony_ci .set_channels = qeth_set_channels, 5338c2ecf20Sopenharmony_ci .get_ts_info = qeth_get_ts_info, 5348c2ecf20Sopenharmony_ci .get_tunable = qeth_get_tunable, 5358c2ecf20Sopenharmony_ci .set_tunable = qeth_set_tunable, 5368c2ecf20Sopenharmony_ci .get_per_queue_coalesce = qeth_get_per_queue_coalesce, 5378c2ecf20Sopenharmony_ci .set_per_queue_coalesce = qeth_set_per_queue_coalesce, 5388c2ecf20Sopenharmony_ci .get_link_ksettings = qeth_get_link_ksettings, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciconst struct ethtool_ops qeth_osn_ethtool_ops = { 5428c2ecf20Sopenharmony_ci .get_strings = qeth_get_strings, 5438c2ecf20Sopenharmony_ci .get_ethtool_stats = qeth_get_ethtool_stats, 5448c2ecf20Sopenharmony_ci .get_sset_count = qeth_get_sset_count, 5458c2ecf20Sopenharmony_ci .get_drvinfo = qeth_get_drvinfo, 5468c2ecf20Sopenharmony_ci}; 547