From 88c060549a4c555d59965801d1e811b71614c2b7 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 1 Mar 2018 02:02:27 +0100 Subject: [PATCH 1/5] dsa: Pass the port to get_sset_count() By passing the port, we allow different ports to have different statistics. This is useful since some ports have SERDES interfaces with their own statistic counters. Signed-off-by: Andrew Lunn Tested-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 2 +- drivers/net/dsa/b53/b53_priv.h | 2 +- drivers/net/dsa/dsa_loop.c | 2 +- drivers/net/dsa/lan9303-core.c | 2 +- drivers/net/dsa/microchip/ksz_common.c | 2 +- drivers/net/dsa/mt7530.c | 2 +- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/dsa/qca8k.c | 2 +- include/net/dsa.h | 2 +- net/dsa/master.c | 4 ++-- net/dsa/slave.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index db830a1141d9..cd16067265dd 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -852,7 +852,7 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) } EXPORT_SYMBOL(b53_get_ethtool_stats); -int b53_get_sset_count(struct dsa_switch *ds) +int b53_get_sset_count(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index d954cf36ecd8..1187ebd79287 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -288,7 +288,7 @@ void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port); int b53_configure_vlan(struct dsa_switch *ds); void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data); void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); -int b53_get_sset_count(struct dsa_switch *ds); +int b53_get_sset_count(struct dsa_switch *ds, int port); int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge); void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge); void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state); diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 7aa84ee4e771..f77be9f85cb3 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -86,7 +86,7 @@ static int dsa_loop_setup(struct dsa_switch *ds) return 0; } -static int dsa_loop_get_sset_count(struct dsa_switch *ds) +static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port) { return __DSA_LOOP_CNT_MAX; } diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 6171c0853ff1..fefa454f3e56 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1007,7 +1007,7 @@ static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port, } } -static int lan9303_get_sset_count(struct dsa_switch *ds) +static int lan9303_get_sset_count(struct dsa_switch *ds, int port) { return ARRAY_SIZE(lan9303_mib); } diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 663b0d5b982b..bcb3e6c734f2 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -439,7 +439,7 @@ static void ksz_disable_port(struct dsa_switch *ds, int port, ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true); } -static int ksz_sset_count(struct dsa_switch *ds) +static int ksz_sset_count(struct dsa_switch *ds, int port) { return TOTAL_SWITCH_COUNTER_NUM; } diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 8a0bb000d056..511ca134f13f 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -604,7 +604,7 @@ mt7530_get_ethtool_stats(struct dsa_switch *ds, int port, } static int -mt7530_get_sset_count(struct dsa_switch *ds) +mt7530_get_sset_count(struct dsa_switch *ds, int port) { return ARRAY_SIZE(mt7530_mib); } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 24486f96dd39..8c9a30d1b06f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -754,7 +754,7 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) STATS_TYPE_BANK1); } -static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 9df22ebee822..600d5ad1fbde 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -631,7 +631,7 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, } static int -qca8k_get_sset_count(struct dsa_switch *ds) +qca8k_get_sset_count(struct dsa_switch *ds, int port) { return ARRAY_SIZE(ar8327_mib); } diff --git a/include/net/dsa.h b/include/net/dsa.h index 0ad17b63684d..60fb4ec8ba61 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -359,7 +359,7 @@ struct dsa_switch_ops { void (*get_strings)(struct dsa_switch *ds, int port, uint8_t *data); void (*get_ethtool_stats)(struct dsa_switch *ds, int port, uint64_t *data); - int (*get_sset_count)(struct dsa_switch *ds); + int (*get_sset_count)(struct dsa_switch *ds, int port); /* * ethtool Wake-on-LAN diff --git a/net/dsa/master.c b/net/dsa/master.c index 00589147f042..90e6df0351eb 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -42,7 +42,7 @@ static int dsa_master_get_sset_count(struct net_device *dev, int sset) count += ops->get_sset_count(dev, sset); if (sset == ETH_SS_STATS && ds->ops->get_sset_count) - count += ds->ops->get_sset_count(ds); + count += ds->ops->get_sset_count(ds, cpu_dp->index); return count; } @@ -76,7 +76,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, * constructed earlier */ ds->ops->get_strings(ds, port, ndata); - count = ds->ops->get_sset_count(ds); + count = ds->ops->get_sset_count(ds, port); for (i = 0; i < count; i++) { memmove(ndata + (i * len + sizeof(pfx)), ndata + i * len, len - sizeof(pfx)); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3376dad6dcfd..18561af7a8f1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -605,7 +605,7 @@ static int dsa_slave_get_sset_count(struct net_device *dev, int sset) count = 4; if (ds->ops->get_sset_count) - count += ds->ops->get_sset_count(ds); + count += ds->ops->get_sset_count(ds, dp->index); return count; } From c6c8cd5e3ce494419d8894d6a96aa17375b83ca2 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 1 Mar 2018 02:02:28 +0100 Subject: [PATCH 2/5] net: dsa: mv88e6xxx: Hold mutex while doing stats operations Until now, there has been no need to hold the reg mutex while getting the count of statistics, or the strings, because the hardware was not accessed. When adding support for SERDES statistics, it is necessary to access the hardware, to determine if a port is using the SERDES interface. So add mutex lock/unlocks. Signed-off-by: Andrew Lunn Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8c9a30d1b06f..27ca0fcb1040 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -724,8 +724,12 @@ static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, { struct mv88e6xxx_chip *chip = ds->priv; + mutex_lock(&chip->reg_lock); + if (chip->info->ops->stats_get_strings) chip->info->ops->stats_get_strings(chip, data); + + mutex_unlock(&chip->reg_lock); } static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip, @@ -757,11 +761,14 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; + int ret = 0; + mutex_lock(&chip->reg_lock); if (chip->info->ops->stats_get_sset_count) - return chip->info->ops->stats_get_sset_count(chip); + ret = chip->info->ops->stats_get_sset_count(chip); + mutex_unlock(&chip->reg_lock); - return 0; + return ret; } static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, From 436fe17d273bed9fd8b7bdf4172ea6d9eac0b703 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 1 Mar 2018 02:02:29 +0100 Subject: [PATCH 3/5] net: dsa: mv88e6xxx: Allow the SERDES interfaces to have statistics When gettting the number of statistics, the strings and the actual statistics, call the SERDES ops if implemented. This means the stats code needs to return the number of strings/stats they have placed into the data, so that the SERDES strings/stats can follow on. Signed-off-by: Andrew Lunn Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 76 ++++++++++++++++++++++---------- drivers/net/dsa/mv88e6xxx/chip.h | 13 ++++-- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 27ca0fcb1040..243d274aace5 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -689,8 +689,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, return value; } -static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip, - uint8_t *data, int types) +static int mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data, int types) { struct mv88e6xxx_hw_stat *stat; int i, j; @@ -703,31 +703,39 @@ static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip, j++; } } + + return j; } -static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip, - uint8_t *data) +static int mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) { - mv88e6xxx_stats_get_strings(chip, data, - STATS_TYPE_BANK0 | STATS_TYPE_PORT); + return mv88e6xxx_stats_get_strings(chip, data, + STATS_TYPE_BANK0 | STATS_TYPE_PORT); } -static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, - uint8_t *data) +static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) { - mv88e6xxx_stats_get_strings(chip, data, - STATS_TYPE_BANK0 | STATS_TYPE_BANK1); + return mv88e6xxx_stats_get_strings(chip, data, + STATS_TYPE_BANK0 | STATS_TYPE_BANK1); } static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) { struct mv88e6xxx_chip *chip = ds->priv; + int count = 0; mutex_lock(&chip->reg_lock); if (chip->info->ops->stats_get_strings) - chip->info->ops->stats_get_strings(chip, data); + count = chip->info->ops->stats_get_strings(chip, data); + + if (chip->info->ops->serdes_get_strings) { + data += count * ETH_GSTRING_LEN; + chip->info->ops->serdes_get_strings(chip, port, data); + } mutex_unlock(&chip->reg_lock); } @@ -761,19 +769,31 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; - int ret = 0; + int serdes_count = 0; + int count = 0; mutex_lock(&chip->reg_lock); if (chip->info->ops->stats_get_sset_count) - ret = chip->info->ops->stats_get_sset_count(chip); + count = chip->info->ops->stats_get_sset_count(chip); + if (count < 0) + goto out; + + if (chip->info->ops->serdes_get_sset_count) + serdes_count = chip->info->ops->serdes_get_sset_count(chip, + port); + if (serdes_count < 0) + count = serdes_count; + else + count += serdes_count; +out: mutex_unlock(&chip->reg_lock); - return ret; + return count; } -static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, - uint64_t *data, int types, - u16 bank1_select, u16 histogram) +static int mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data, int types, + u16 bank1_select, u16 histogram) { struct mv88e6xxx_hw_stat *stat; int i, j; @@ -790,18 +810,19 @@ static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, j++; } } + return j; } -static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, - uint64_t *data) +static int mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) { return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0 | STATS_TYPE_PORT, 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); } -static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, - uint64_t *data) +static int mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) { return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0 | STATS_TYPE_BANK1, @@ -809,8 +830,8 @@ static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); } -static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port, - uint64_t *data) +static int mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) { return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0 | STATS_TYPE_BANK1, @@ -821,8 +842,15 @@ static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port, static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data) { + int count = 0; + if (chip->info->ops->stats_get_stats) - chip->info->ops->stats_get_stats(chip, port, data); + count = chip->info->ops->stats_get_stats(chip, port, data); + + if (chip->info->ops->serdes_get_stats) { + data += count; + chip->info->ops->serdes_get_stats(chip, port, data); + } } static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index d6a1391dc268..e1ed1b7ca319 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -386,9 +386,9 @@ struct mv88e6xxx_ops { /* Return the number of strings describing statistics */ int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip); - void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); - void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, - uint64_t *data); + int (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); + int (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port); int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port); const struct mv88e6xxx_irq_ops *watchdog_ops; @@ -398,6 +398,13 @@ struct mv88e6xxx_ops { /* Power on/off a SERDES interface */ int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on); + /* Statistics from the SERDES interface */ + int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); + void (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port, + uint8_t *data); + void (*serdes_get_stats)(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); + /* VLAN Translation Unit operations */ int (*vtu_getnext)(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); From eb755c3f6b7deb22ed19e2d3cc4418d3ae510196 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 1 Mar 2018 02:02:30 +0100 Subject: [PATCH 4/5] net: dsa: mv88e6xxx: Add helper to determining if port has SERDES Refactor the existing code. This helper will be used for SERDES statistics. Signed-off-by: Andrew Lunn Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/serdes.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index f3c01119b3d1..4487d18132a4 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -55,18 +55,30 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) return err; } -int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) { - int err; u8 cmode; + int err; err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + if (err) { + dev_err(chip->dev, "failed to read cmode\n"); + return 0; + } if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || - (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) { + (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) + return 1; + + return 0; +} + +int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + int err; + + if (mv88e6352_port_has_serdes(chip, port)) { err = mv88e6352_serdes_power_set(chip, on); if (err < 0) return err; From cda9f4aae3dceed192442807b70a5d34a13c478b Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 1 Mar 2018 02:02:31 +0100 Subject: [PATCH 5/5] net: dsa: mv88e6xxx: Get mv88e6352 SERDES statistics 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 Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 7 ++- drivers/net/dsa/mv88e6xxx/chip.h | 10 +++- drivers/net/dsa/mv88e6xxx/serdes.c | 84 ++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/serdes.h | 6 ++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 243d274aace5..cfd53632a655 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -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, ®); 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 = { diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index e1ed1b7ca319..26b9a618cdee 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -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; }; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 4487d18132a4..4756969c1546 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -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, ®); + 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, ®); + 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) { diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 5c1cd6d8e9a5..641baa75f910 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -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