net: dsa: mv88e6xxx: Get mv88e6352 SERDES statistics
authorAndrew Lunn <andrew@lunn.ch>
Thu, 1 Mar 2018 01:02:31 +0000 (02:02 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 4 Mar 2018 18:34:18 +0000 (13:34 -0500)
Add support for reading the SERDES statistics of the mv88e8352, using
the standard ethtool -S option. The SERDES interface can be mapped to
either port 4 or 5, so only return statistics on those ports, if the
SERDES interface is in use.

The counters are reset on read, so need to be accumulated. Add a per
port structure to hold the stats counters. The 6352 only has a single
SERDES interface and so only one port will using the newly added
array. However the 6390 family has as many SERDES interfaces as ports,
each with statistics counters. Also, PTP has a number of counters per
port which will also need accumulating.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/chip.h
drivers/net/dsa/mv88e6xxx/serdes.c
drivers/net/dsa/mv88e6xxx/serdes.h

index 243d274aace5eacd8ecb325f1df5eeac731b52bb..cfd53632a655635882fe69b1cccf30cc6bd439d7 100644 (file)
@@ -666,7 +666,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
                        return UINT64_MAX;
 
                low = reg;
-               if (s->sizeof_stat == 4) {
+               if (s->size == 4) {
                        err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
                        if (err)
                                return UINT64_MAX;
@@ -679,7 +679,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
        case STATS_TYPE_BANK0:
                reg |= s->reg | histogram;
                mv88e6xxx_g1_stats_read(chip, reg, &low);
-               if (s->sizeof_stat == 8)
+               if (s->size == 8)
                        mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
                break;
        default:
@@ -3225,6 +3225,9 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
        .serdes_power = mv88e6352_serdes_power,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6352_avb_ops,
+       .serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
+       .serdes_get_strings = mv88e6352_serdes_get_strings,
+       .serdes_get_stats = mv88e6352_serdes_get_stats,
 };
 
 static const struct mv88e6xxx_ops mv88e6390_ops = {
index e1ed1b7ca319a4619c8ecfd490515d435d7e666c..26b9a618cdee1830aa908529552d9413e83b8158 100644 (file)
@@ -191,6 +191,10 @@ struct mv88e6xxx_port_hwtstamp {
        struct hwtstamp_config tstamp_config;
 };
 
+struct mv88e6xxx_port {
+       u64 serdes_stats[2];
+};
+
 struct mv88e6xxx_chip {
        const struct mv88e6xxx_info *info;
 
@@ -244,6 +248,7 @@ struct mv88e6xxx_chip {
        int irq;
        int device_irq;
        int watchdog_irq;
+
        int atu_prob_irq;
        int vtu_prob_irq;
        struct kthread_worker *kworker;
@@ -268,6 +273,9 @@ struct mv88e6xxx_chip {
 
        /* Per-port timestamping resources. */
        struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
+
+       /* Array of port structures. */
+       struct mv88e6xxx_port ports[DSA_MAX_PORTS];
 };
 
 struct mv88e6xxx_bus_ops {
@@ -469,7 +477,7 @@ struct mv88e6xxx_avb_ops {
 
 struct mv88e6xxx_hw_stat {
        char string[ETH_GSTRING_LEN];
-       int sizeof_stat;
+       size_t size;
        int reg;
        int type;
 };
index 4487d18132a4a52cdd2a887be920e3ee28947eb2..4756969c154629eebb9ff108eba4bf641f005d67 100644 (file)
@@ -87,6 +87,90 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
        return 0;
 }
 
+struct mv88e6352_serdes_hw_stat {
+       char string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int reg;
+};
+
+static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
+       { "serdes_fibre_rx_error", 16, 21 },
+       { "serdes_PRBS_error", 32, 24 },
+};
+
+int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
+{
+       if (mv88e6352_port_has_serdes(chip, port))
+               return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
+
+       return 0;
+}
+
+void mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+                                 int port, uint8_t *data)
+{
+       struct mv88e6352_serdes_hw_stat *stat;
+       int i;
+
+       if (!mv88e6352_port_has_serdes(chip, port))
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
+               stat = &mv88e6352_serdes_hw_stats[i];
+               memcpy(data + i * ETH_GSTRING_LEN, stat->string,
+                      ETH_GSTRING_LEN);
+       }
+}
+
+static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
+                                         struct mv88e6352_serdes_hw_stat *stat)
+{
+       u64 val = 0;
+       u16 reg;
+       int err;
+
+       err = mv88e6352_serdes_read(chip, stat->reg, &reg);
+       if (err) {
+               dev_err(chip->dev, "failed to read statistic\n");
+               return 0;
+       }
+
+       val = reg;
+
+       if (stat->sizeof_stat == 32) {
+               err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
+               if (err) {
+                       dev_err(chip->dev, "failed to read statistic\n");
+                       return 0;
+               }
+               val = val << 16 | reg;
+       }
+
+       return val;
+}
+
+void mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
+                               uint64_t *data)
+{
+       struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
+       struct mv88e6352_serdes_hw_stat *stat;
+       u64 value;
+       int i;
+
+       if (!mv88e6352_port_has_serdes(chip, port))
+               return;
+
+       BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
+                    ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
+
+       for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
+               stat = &mv88e6352_serdes_hw_stats[i];
+               value = mv88e6352_serdes_get_stat(chip, stat);
+               mv88e6xxx_port->serdes_stats[i] += value;
+               data[i] = mv88e6xxx_port->serdes_stats[i];
+       }
+}
+
 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
 {
index 5c1cd6d8e9a5e361971d23ffead531712174ed06..641baa75f910842de57b346346f13b02db744828 100644 (file)
@@ -44,5 +44,9 @@
 
 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
-
+int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
+void mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+                                 int port, uint8_t *data);
+void mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
+                               uint64_t *data);
 #endif