1f2f23566SVivien Didelot /* 2f2f23566SVivien Didelot * Handling of a master device, switching frames via its switch fabric CPU port 3f2f23566SVivien Didelot * 4f2f23566SVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 5f2f23566SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6f2f23566SVivien Didelot * 7f2f23566SVivien Didelot * This program is free software; you can redistribute it and/or modify 8f2f23566SVivien Didelot * it under the terms of the GNU General Public License as published by 9f2f23566SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 10f2f23566SVivien Didelot * (at your option) any later version. 11f2f23566SVivien Didelot */ 12f2f23566SVivien Didelot 13f2f23566SVivien Didelot #include "dsa_priv.h" 14f2f23566SVivien Didelot 15f2f23566SVivien Didelot static void dsa_master_get_ethtool_stats(struct net_device *dev, 16f2f23566SVivien Didelot struct ethtool_stats *stats, 17f2f23566SVivien Didelot uint64_t *data) 18f2f23566SVivien Didelot { 192f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 207ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 217ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 227ec764eeSVivien Didelot int port = cpu_dp->index; 23f2f23566SVivien Didelot int count = 0; 24f2f23566SVivien Didelot 251d1e79f1SFlorian Fainelli if (ops->get_sset_count && ops->get_ethtool_stats) { 26f2f23566SVivien Didelot count = ops->get_sset_count(dev, ETH_SS_STATS); 27f2f23566SVivien Didelot ops->get_ethtool_stats(dev, stats, data); 28f2f23566SVivien Didelot } 29f2f23566SVivien Didelot 30f2f23566SVivien Didelot if (ds->ops->get_ethtool_stats) 317ec764eeSVivien Didelot ds->ops->get_ethtool_stats(ds, port, data + count); 32f2f23566SVivien Didelot } 33f2f23566SVivien Didelot 34cf963573SFlorian Fainelli static void dsa_master_get_ethtool_phy_stats(struct net_device *dev, 35cf963573SFlorian Fainelli struct ethtool_stats *stats, 36cf963573SFlorian Fainelli uint64_t *data) 37cf963573SFlorian Fainelli { 38cf963573SFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 39cf963573SFlorian Fainelli const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 40cf963573SFlorian Fainelli struct dsa_switch *ds = cpu_dp->ds; 41cf963573SFlorian Fainelli int port = cpu_dp->index; 42cf963573SFlorian Fainelli int count = 0; 43cf963573SFlorian Fainelli 44cf963573SFlorian Fainelli if (dev->phydev && !ops->get_ethtool_phy_stats) { 45cf963573SFlorian Fainelli count = phy_ethtool_get_sset_count(dev->phydev); 46cf963573SFlorian Fainelli if (count >= 0) 47cf963573SFlorian Fainelli phy_ethtool_get_stats(dev->phydev, stats, data); 48cf963573SFlorian Fainelli } else if (ops->get_sset_count && ops->get_ethtool_phy_stats) { 49cf963573SFlorian Fainelli count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); 50cf963573SFlorian Fainelli ops->get_ethtool_phy_stats(dev, stats, data); 51cf963573SFlorian Fainelli } 52cf963573SFlorian Fainelli 53cf963573SFlorian Fainelli if (count < 0) 54cf963573SFlorian Fainelli count = 0; 55cf963573SFlorian Fainelli 56cf963573SFlorian Fainelli if (ds->ops->get_ethtool_phy_stats) 57cf963573SFlorian Fainelli ds->ops->get_ethtool_phy_stats(ds, port, data + count); 58cf963573SFlorian Fainelli } 59cf963573SFlorian Fainelli 60f2f23566SVivien Didelot static int dsa_master_get_sset_count(struct net_device *dev, int sset) 61f2f23566SVivien Didelot { 622f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 637ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 647ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 65f2f23566SVivien Didelot int count = 0; 66f2f23566SVivien Didelot 67cf963573SFlorian Fainelli if (sset == ETH_SS_PHY_STATS && dev->phydev && 68cf963573SFlorian Fainelli !ops->get_ethtool_phy_stats) 69cf963573SFlorian Fainelli count = phy_ethtool_get_sset_count(dev->phydev); 70cf963573SFlorian Fainelli else if (ops->get_sset_count) 7189f09048SFlorian Fainelli count = ops->get_sset_count(dev, sset); 72cf963573SFlorian Fainelli 7389f09048SFlorian Fainelli if (count < 0) 7489f09048SFlorian Fainelli count = 0; 75f2f23566SVivien Didelot 7689f09048SFlorian Fainelli if (ds->ops->get_sset_count) 7789f09048SFlorian Fainelli count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); 78f2f23566SVivien Didelot 79f2f23566SVivien Didelot return count; 80f2f23566SVivien Didelot } 81f2f23566SVivien Didelot 82f2f23566SVivien Didelot static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, 83f2f23566SVivien Didelot uint8_t *data) 84f2f23566SVivien Didelot { 852f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 867ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 877ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 887ec764eeSVivien Didelot int port = cpu_dp->index; 89f2f23566SVivien Didelot int len = ETH_GSTRING_LEN; 90f2f23566SVivien Didelot int mcount = 0, count; 91f2f23566SVivien Didelot unsigned int i; 92f2f23566SVivien Didelot uint8_t pfx[4]; 93f2f23566SVivien Didelot uint8_t *ndata; 94f2f23566SVivien Didelot 957ec764eeSVivien Didelot snprintf(pfx, sizeof(pfx), "p%.2d", port); 96f2f23566SVivien Didelot /* We do not want to be NULL-terminated, since this is a prefix */ 97f2f23566SVivien Didelot pfx[sizeof(pfx) - 1] = '_'; 98f2f23566SVivien Didelot 99cf963573SFlorian Fainelli if (stringset == ETH_SS_PHY_STATS && dev->phydev && 100cf963573SFlorian Fainelli !ops->get_ethtool_phy_stats) { 101cf963573SFlorian Fainelli mcount = phy_ethtool_get_sset_count(dev->phydev); 102cf963573SFlorian Fainelli if (mcount < 0) 103cf963573SFlorian Fainelli mcount = 0; 104cf963573SFlorian Fainelli else 105cf963573SFlorian Fainelli phy_ethtool_get_strings(dev->phydev, data); 106cf963573SFlorian Fainelli } else if (ops->get_sset_count && ops->get_strings) { 10789f09048SFlorian Fainelli mcount = ops->get_sset_count(dev, stringset); 10889f09048SFlorian Fainelli if (mcount < 0) 10989f09048SFlorian Fainelli mcount = 0; 110f2f23566SVivien Didelot ops->get_strings(dev, stringset, data); 111f2f23566SVivien Didelot } 112f2f23566SVivien Didelot 11389f09048SFlorian Fainelli if (ds->ops->get_strings) { 114f2f23566SVivien Didelot ndata = data + mcount * len; 115f2f23566SVivien Didelot /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 116f2f23566SVivien Didelot * the output after to prepend our CPU port prefix we 117f2f23566SVivien Didelot * constructed earlier 118f2f23566SVivien Didelot */ 11989f09048SFlorian Fainelli ds->ops->get_strings(ds, port, stringset, ndata); 12089f09048SFlorian Fainelli count = ds->ops->get_sset_count(ds, port, stringset); 121f2f23566SVivien Didelot for (i = 0; i < count; i++) { 122f2f23566SVivien Didelot memmove(ndata + (i * len + sizeof(pfx)), 123f2f23566SVivien Didelot ndata + i * len, len - sizeof(pfx)); 124f2f23566SVivien Didelot memcpy(ndata + i * len, pfx, sizeof(pfx)); 125f2f23566SVivien Didelot } 126f2f23566SVivien Didelot } 127f2f23566SVivien Didelot } 128f2f23566SVivien Didelot 12917a22fcfSVivien Didelot static int dsa_master_ethtool_setup(struct net_device *dev) 130f2f23566SVivien Didelot { 1312f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 1327ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 133f2f23566SVivien Didelot struct ethtool_ops *ops; 134f2f23566SVivien Didelot 135f2f23566SVivien Didelot ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 136f2f23566SVivien Didelot if (!ops) 137f2f23566SVivien Didelot return -ENOMEM; 138f2f23566SVivien Didelot 1397ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = dev->ethtool_ops; 1407ec764eeSVivien Didelot if (cpu_dp->orig_ethtool_ops) 1417ec764eeSVivien Didelot memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); 142f2f23566SVivien Didelot 143f2f23566SVivien Didelot ops->get_sset_count = dsa_master_get_sset_count; 144f2f23566SVivien Didelot ops->get_ethtool_stats = dsa_master_get_ethtool_stats; 145f2f23566SVivien Didelot ops->get_strings = dsa_master_get_strings; 146cf963573SFlorian Fainelli ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats; 147f2f23566SVivien Didelot 148f2f23566SVivien Didelot dev->ethtool_ops = ops; 149f2f23566SVivien Didelot 150f2f23566SVivien Didelot return 0; 151f2f23566SVivien Didelot } 152f2f23566SVivien Didelot 15317a22fcfSVivien Didelot static void dsa_master_ethtool_teardown(struct net_device *dev) 154f2f23566SVivien Didelot { 1552f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 156f2f23566SVivien Didelot 1577ec764eeSVivien Didelot dev->ethtool_ops = cpu_dp->orig_ethtool_ops; 1587ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = NULL; 159f2f23566SVivien Didelot } 16017a22fcfSVivien Didelot 161*a3d7e01dSFlorian Fainelli static ssize_t tagging_show(struct device *d, struct device_attribute *attr, 162*a3d7e01dSFlorian Fainelli char *buf) 163*a3d7e01dSFlorian Fainelli { 164*a3d7e01dSFlorian Fainelli struct net_device *dev = to_net_dev(d); 165*a3d7e01dSFlorian Fainelli struct dsa_port *cpu_dp = dev->dsa_ptr; 166*a3d7e01dSFlorian Fainelli 167*a3d7e01dSFlorian Fainelli return sprintf(buf, "%s\n", 168*a3d7e01dSFlorian Fainelli dsa_tag_protocol_to_str(cpu_dp->tag_ops)); 169*a3d7e01dSFlorian Fainelli } 170*a3d7e01dSFlorian Fainelli static DEVICE_ATTR_RO(tagging); 171*a3d7e01dSFlorian Fainelli 172*a3d7e01dSFlorian Fainelli static struct attribute *dsa_slave_attrs[] = { 173*a3d7e01dSFlorian Fainelli &dev_attr_tagging.attr, 174*a3d7e01dSFlorian Fainelli NULL 175*a3d7e01dSFlorian Fainelli }; 176*a3d7e01dSFlorian Fainelli 177*a3d7e01dSFlorian Fainelli static const struct attribute_group dsa_group = { 178*a3d7e01dSFlorian Fainelli .name = "dsa", 179*a3d7e01dSFlorian Fainelli .attrs = dsa_slave_attrs, 180*a3d7e01dSFlorian Fainelli }; 181*a3d7e01dSFlorian Fainelli 18217a22fcfSVivien Didelot int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) 18317a22fcfSVivien Didelot { 184*a3d7e01dSFlorian Fainelli int ret; 185*a3d7e01dSFlorian Fainelli 18617a22fcfSVivien Didelot /* If we use a tagging format that doesn't have an ethertype 18717a22fcfSVivien Didelot * field, make sure that all packets from this point on get 18817a22fcfSVivien Didelot * sent to the tag format's receive function. 18917a22fcfSVivien Didelot */ 19017a22fcfSVivien Didelot wmb(); 19117a22fcfSVivien Didelot 19217a22fcfSVivien Didelot dev->dsa_ptr = cpu_dp; 19317a22fcfSVivien Didelot 194*a3d7e01dSFlorian Fainelli ret = dsa_master_ethtool_setup(dev); 195*a3d7e01dSFlorian Fainelli if (ret) 196*a3d7e01dSFlorian Fainelli return ret; 197*a3d7e01dSFlorian Fainelli 198*a3d7e01dSFlorian Fainelli ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); 199*a3d7e01dSFlorian Fainelli if (ret) 200*a3d7e01dSFlorian Fainelli dsa_master_ethtool_teardown(dev); 201*a3d7e01dSFlorian Fainelli 202*a3d7e01dSFlorian Fainelli return ret; 20317a22fcfSVivien Didelot } 20417a22fcfSVivien Didelot 20517a22fcfSVivien Didelot void dsa_master_teardown(struct net_device *dev) 20617a22fcfSVivien Didelot { 207*a3d7e01dSFlorian Fainelli sysfs_remove_group(&dev->dev.kobj, &dsa_group); 20817a22fcfSVivien Didelot dsa_master_ethtool_teardown(dev); 20917a22fcfSVivien Didelot 21017a22fcfSVivien Didelot dev->dsa_ptr = NULL; 21117a22fcfSVivien Didelot 21217a22fcfSVivien Didelot /* If we used a tagging format that doesn't have an ethertype 21317a22fcfSVivien Didelot * field, make sure that all packets from this point get sent 21417a22fcfSVivien Didelot * without the tag and go through the regular receive path. 21517a22fcfSVivien Didelot */ 21617a22fcfSVivien Didelot wmb(); 21717a22fcfSVivien Didelot } 218