linux/drivers/staging/mt7621-eth/ethtool.c
<<
>>
Prefs
   1/*   This program is free software; you can redistribute it and/or modify
   2 *   it under the terms of the GNU General Public License as published by
   3 *   the Free Software Foundation; version 2 of the License
   4 *
   5 *   This program is distributed in the hope that it will be useful,
   6 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   7 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   8 *   GNU General Public License for more details.
   9 *
  10 *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
  11 *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
  12 *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
  13 */
  14
  15#include "mtk_eth_soc.h"
  16#include "ethtool.h"
  17
  18struct mtk_stat {
  19        char name[ETH_GSTRING_LEN];
  20        unsigned int idx;
  21};
  22
  23#define MTK_HW_STAT(stat) { \
  24        .name = #stat, \
  25        .idx = offsetof(struct mtk_hw_stats, stat) / sizeof(u64) \
  26}
  27
  28static const struct mtk_stat mtk_ethtool_hw_stats[] = {
  29        MTK_HW_STAT(tx_bytes),
  30        MTK_HW_STAT(tx_packets),
  31        MTK_HW_STAT(tx_skip),
  32        MTK_HW_STAT(tx_collisions),
  33        MTK_HW_STAT(rx_bytes),
  34        MTK_HW_STAT(rx_packets),
  35        MTK_HW_STAT(rx_overflow),
  36        MTK_HW_STAT(rx_fcs_errors),
  37        MTK_HW_STAT(rx_short_errors),
  38        MTK_HW_STAT(rx_long_errors),
  39        MTK_HW_STAT(rx_checksum_errors),
  40        MTK_HW_STAT(rx_flow_control_packets),
  41};
  42
  43#define MTK_HW_STATS_LEN        ARRAY_SIZE(mtk_ethtool_hw_stats)
  44
  45static int mtk_get_link_ksettings(struct net_device *dev,
  46                                  struct ethtool_link_ksettings *cmd)
  47{
  48        struct mtk_mac *mac = netdev_priv(dev);
  49        int err;
  50
  51        if (!mac->phy_dev)
  52                return -ENODEV;
  53
  54        if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
  55                err = phy_read_status(mac->phy_dev);
  56                if (err)
  57                        return -ENODEV;
  58        }
  59
  60        phy_ethtool_ksettings_get(mac->phy_dev, cmd);
  61        return 0;
  62}
  63
  64static int mtk_set_link_ksettings(struct net_device *dev,
  65                                  const struct ethtool_link_ksettings *cmd)
  66{
  67        struct mtk_mac *mac = netdev_priv(dev);
  68
  69        if (!mac->phy_dev)
  70                return -ENODEV;
  71
  72        if (cmd->base.phy_address != mac->phy_dev->mdio.addr) {
  73                if (mac->hw->phy->phy_node[cmd->base.phy_address]) {
  74                        mac->phy_dev = mac->hw->phy->phy[cmd->base.phy_address];
  75                        mac->phy_flags = MTK_PHY_FLAG_PORT;
  76                } else if (mac->hw->mii_bus) {
  77                        mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
  78                                                       cmd->base.phy_address);
  79                        if (!mac->phy_dev)
  80                                return -ENODEV;
  81                        mac->phy_flags = MTK_PHY_FLAG_ATTACH;
  82                } else {
  83                        return -ENODEV;
  84                }
  85        }
  86
  87        return phy_ethtool_ksettings_set(mac->phy_dev, cmd);
  88}
  89
  90static void mtk_get_drvinfo(struct net_device *dev,
  91                            struct ethtool_drvinfo *info)
  92{
  93        struct mtk_mac *mac = netdev_priv(dev);
  94        struct mtk_soc_data *soc = mac->hw->soc;
  95
  96        strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
  97        strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
  98
  99        if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE])
 100                info->n_stats = MTK_HW_STATS_LEN;
 101}
 102
 103static u32 mtk_get_msglevel(struct net_device *dev)
 104{
 105        struct mtk_mac *mac = netdev_priv(dev);
 106
 107        return mac->hw->msg_enable;
 108}
 109
 110static void mtk_set_msglevel(struct net_device *dev, u32 value)
 111{
 112        struct mtk_mac *mac = netdev_priv(dev);
 113
 114        mac->hw->msg_enable = value;
 115}
 116
 117static int mtk_nway_reset(struct net_device *dev)
 118{
 119        struct mtk_mac *mac = netdev_priv(dev);
 120
 121        if (!mac->phy_dev)
 122                return -EOPNOTSUPP;
 123
 124        return genphy_restart_aneg(mac->phy_dev);
 125}
 126
 127static u32 mtk_get_link(struct net_device *dev)
 128{
 129        struct mtk_mac *mac = netdev_priv(dev);
 130        int err;
 131
 132        if (!mac->phy_dev)
 133                goto out_get_link;
 134
 135        if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
 136                err = genphy_update_link(mac->phy_dev);
 137                if (err)
 138                        goto out_get_link;
 139        }
 140
 141        return mac->phy_dev->link;
 142
 143out_get_link:
 144        return ethtool_op_get_link(dev);
 145}
 146
 147static int mtk_set_ringparam(struct net_device *dev,
 148                             struct ethtool_ringparam *ring)
 149{
 150        struct mtk_mac *mac = netdev_priv(dev);
 151
 152        if ((ring->tx_pending < 2) ||
 153            (ring->rx_pending < 2) ||
 154            (ring->rx_pending > mac->hw->soc->dma_ring_size) ||
 155            (ring->tx_pending > mac->hw->soc->dma_ring_size))
 156                return -EINVAL;
 157
 158        dev->netdev_ops->ndo_stop(dev);
 159
 160        mac->hw->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
 161        mac->hw->rx_ring[0].rx_ring_size = BIT(fls(ring->rx_pending) - 1);
 162
 163        return dev->netdev_ops->ndo_open(dev);
 164}
 165
 166static void mtk_get_ringparam(struct net_device *dev,
 167                              struct ethtool_ringparam *ring)
 168{
 169        struct mtk_mac *mac = netdev_priv(dev);
 170
 171        ring->rx_max_pending = mac->hw->soc->dma_ring_size;
 172        ring->tx_max_pending = mac->hw->soc->dma_ring_size;
 173        ring->rx_pending = mac->hw->rx_ring[0].rx_ring_size;
 174        ring->tx_pending = mac->hw->tx_ring.tx_ring_size;
 175}
 176
 177static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
 178{
 179        int i;
 180
 181        switch (stringset) {
 182        case ETH_SS_STATS:
 183                for (i = 0; i < MTK_HW_STATS_LEN; i++) {
 184                        memcpy(data, mtk_ethtool_hw_stats[i].name,
 185                               ETH_GSTRING_LEN);
 186                        data += ETH_GSTRING_LEN;
 187                }
 188                break;
 189        }
 190}
 191
 192static int mtk_get_sset_count(struct net_device *dev, int sset)
 193{
 194        switch (sset) {
 195        case ETH_SS_STATS:
 196                return MTK_HW_STATS_LEN;
 197        default:
 198                return -EOPNOTSUPP;
 199        }
 200}
 201
 202static void mtk_get_ethtool_stats(struct net_device *dev,
 203                                  struct ethtool_stats *stats, u64 *data)
 204{
 205        struct mtk_mac *mac = netdev_priv(dev);
 206        struct mtk_hw_stats *hwstats = mac->hw_stats;
 207        unsigned int start;
 208        int i;
 209
 210        if (netif_running(dev) && netif_device_present(dev)) {
 211                if (spin_trylock(&hwstats->stats_lock)) {
 212                        mtk_stats_update_mac(mac);
 213                        spin_unlock(&hwstats->stats_lock);
 214                }
 215        }
 216
 217        do {
 218                start = u64_stats_fetch_begin_irq(&hwstats->syncp);
 219                for (i = 0; i < MTK_HW_STATS_LEN; i++)
 220                        data[i] = ((u64 *)hwstats)[mtk_ethtool_hw_stats[i].idx];
 221
 222        } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
 223}
 224
 225static struct ethtool_ops mtk_ethtool_ops = {
 226        .get_link_ksettings     = mtk_get_link_ksettings,
 227        .set_link_ksettings     = mtk_set_link_ksettings,
 228        .get_drvinfo            = mtk_get_drvinfo,
 229        .get_msglevel           = mtk_get_msglevel,
 230        .set_msglevel           = mtk_set_msglevel,
 231        .nway_reset             = mtk_nway_reset,
 232        .get_link               = mtk_get_link,
 233        .set_ringparam          = mtk_set_ringparam,
 234        .get_ringparam          = mtk_get_ringparam,
 235};
 236
 237void mtk_set_ethtool_ops(struct net_device *netdev)
 238{
 239        struct mtk_mac *mac = netdev_priv(netdev);
 240        struct mtk_soc_data *soc = mac->hw->soc;
 241
 242        if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) {
 243                mtk_ethtool_ops.get_strings = mtk_get_strings;
 244                mtk_ethtool_ops.get_sset_count = mtk_get_sset_count;
 245                mtk_ethtool_ops.get_ethtool_stats = mtk_get_ethtool_stats;
 246        }
 247
 248        netdev->ethtool_ops = &mtk_ethtool_ops;
 249}
 250