Commit f5e2ed02 authored by Andrew Lunn's avatar Andrew Lunn Committed by David S. Miller

dsa: mv88e6xxx: Add Second back of statistics

The 6320 family of switch chips has a second bank for statistics, but
is missing three statistics in the port registers. Generalise and
extend the code:

* adding a field to the statistics table indicating the bank/register
  set where each statistics is.
* add a function indicating if an individual statistics
  is available on this device
* calculate at run time the sset_count.
* return strings based on the available statistics of the device
* return statistics based on the available statistics of the device
* Add support for reading from the second bank.
Signed-off-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ff5756d0
...@@ -617,98 +617,112 @@ static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) ...@@ -617,98 +617,112 @@ static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
} }
static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
{ "in_good_octets", 8, 0x00, }, { "in_good_octets", 8, 0x00, BANK0, },
{ "in_bad_octets", 4, 0x02, }, { "in_bad_octets", 4, 0x02, BANK0, },
{ "in_unicast", 4, 0x04, }, { "in_unicast", 4, 0x04, BANK0, },
{ "in_broadcasts", 4, 0x06, }, { "in_broadcasts", 4, 0x06, BANK0, },
{ "in_multicasts", 4, 0x07, }, { "in_multicasts", 4, 0x07, BANK0, },
{ "in_pause", 4, 0x16, }, { "in_pause", 4, 0x16, BANK0, },
{ "in_undersize", 4, 0x18, }, { "in_undersize", 4, 0x18, BANK0, },
{ "in_fragments", 4, 0x19, }, { "in_fragments", 4, 0x19, BANK0, },
{ "in_oversize", 4, 0x1a, }, { "in_oversize", 4, 0x1a, BANK0, },
{ "in_jabber", 4, 0x1b, }, { "in_jabber", 4, 0x1b, BANK0, },
{ "in_rx_error", 4, 0x1c, }, { "in_rx_error", 4, 0x1c, BANK0, },
{ "in_fcs_error", 4, 0x1d, }, { "in_fcs_error", 4, 0x1d, BANK0, },
{ "out_octets", 8, 0x0e, }, { "out_octets", 8, 0x0e, BANK0, },
{ "out_unicast", 4, 0x10, }, { "out_unicast", 4, 0x10, BANK0, },
{ "out_broadcasts", 4, 0x13, }, { "out_broadcasts", 4, 0x13, BANK0, },
{ "out_multicasts", 4, 0x12, }, { "out_multicasts", 4, 0x12, BANK0, },
{ "out_pause", 4, 0x15, }, { "out_pause", 4, 0x15, BANK0, },
{ "excessive", 4, 0x11, }, { "excessive", 4, 0x11, BANK0, },
{ "collisions", 4, 0x1e, }, { "collisions", 4, 0x1e, BANK0, },
{ "deferred", 4, 0x05, }, { "deferred", 4, 0x05, BANK0, },
{ "single", 4, 0x14, }, { "single", 4, 0x14, BANK0, },
{ "multiple", 4, 0x17, }, { "multiple", 4, 0x17, BANK0, },
{ "out_fcs_error", 4, 0x03, }, { "out_fcs_error", 4, 0x03, BANK0, },
{ "late", 4, 0x1f, }, { "late", 4, 0x1f, BANK0, },
{ "hist_64bytes", 4, 0x08, }, { "hist_64bytes", 4, 0x08, BANK0, },
{ "hist_65_127bytes", 4, 0x09, }, { "hist_65_127bytes", 4, 0x09, BANK0, },
{ "hist_128_255bytes", 4, 0x0a, }, { "hist_128_255bytes", 4, 0x0a, BANK0, },
{ "hist_256_511bytes", 4, 0x0b, }, { "hist_256_511bytes", 4, 0x0b, BANK0, },
{ "hist_512_1023bytes", 4, 0x0c, }, { "hist_512_1023bytes", 4, 0x0c, BANK0, },
{ "hist_1024_max_bytes", 4, 0x0d, }, { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
/* Not all devices have the following counters */ { "sw_in_discards", 4, 0x10, PORT, },
{ "sw_in_discards", 4, 0x110, }, { "sw_in_filtered", 2, 0x12, PORT, },
{ "sw_in_filtered", 2, 0x112, }, { "sw_out_filtered", 2, 0x13, PORT, },
{ "sw_out_filtered", 2, 0x113, }, { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
{ "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
}; };
static bool have_sw_in_discards(struct dsa_switch *ds) static bool mv88e6xxx_has_stat(struct dsa_switch *ds,
struct mv88e6xxx_hw_stat *stat)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); switch (stat->type) {
case BANK0:
switch (ps->id) {
case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
case PORT_SWITCH_ID_6352:
return true; return true;
default: case BANK1:
return false; return mv88e6xxx_6320_family(ds);
} case PORT:
} return mv88e6xxx_6095_family(ds) ||
mv88e6xxx_6185_family(ds) ||
static void _mv88e6xxx_get_strings(struct dsa_switch *ds, mv88e6xxx_6097_family(ds) ||
int nr_stats, mv88e6xxx_6165_family(ds) ||
struct mv88e6xxx_hw_stat *stats, mv88e6xxx_6351_family(ds) ||
int port, uint8_t *data) mv88e6xxx_6352_family(ds);
{
int i;
for (i = 0; i < nr_stats; i++) {
memcpy(data + i * ETH_GSTRING_LEN,
stats[i].string, ETH_GSTRING_LEN);
} }
return false;
} }
static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
int stat, struct mv88e6xxx_hw_stat *s,
struct mv88e6xxx_hw_stat *stats,
int port) int port)
{ {
struct mv88e6xxx_hw_stat *s = stats + stat;
u32 low; u32 low;
u32 high = 0; u32 high = 0;
int ret; int ret;
u64 value; u64 value;
if (s->reg >= 0x100) { switch (s->type) {
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), case PORT:
s->reg - 0x100); ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg);
if (ret < 0) if (ret < 0)
return UINT64_MAX; return UINT64_MAX;
low = ret; low = ret;
if (s->sizeof_stat == 4) { if (s->sizeof_stat == 4) {
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
s->reg - 0x100 + 1); s->reg + 1);
if (ret < 0) if (ret < 0)
return UINT64_MAX; return UINT64_MAX;
high = ret; high = ret;
} }
} else { break;
case BANK0:
case BANK1:
_mv88e6xxx_stats_read(ds, s->reg, &low); _mv88e6xxx_stats_read(ds, s->reg, &low);
if (s->sizeof_stat == 8) if (s->sizeof_stat == 8)
_mv88e6xxx_stats_read(ds, s->reg + 1, &high); _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
...@@ -717,61 +731,59 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, ...@@ -717,61 +731,59 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
return value; return value;
} }
static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
int nr_stats,
struct mv88e6xxx_hw_stat *stats,
int port, uint64_t *data)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat;
int ret; int i, j;
int i;
mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_stats_snapshot(ds, port); for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
if (ret < 0) { stat = &mv88e6xxx_hw_stats[i];
mutex_unlock(&ps->smi_mutex); if (mv88e6xxx_has_stat(ds, stat)) {
return; memcpy(data + j * ETH_GSTRING_LEN, stat->string,
ETH_GSTRING_LEN);
j++;
}
} }
/* Read each of the counters. */
for (i = 0; i < nr_stats; i++)
data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
mutex_unlock(&ps->smi_mutex);
}
/* All the statistics in the table */
void
mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
if (have_sw_in_discards(ds))
_mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
mv88e6xxx_hw_stats, port, data);
else
_mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
mv88e6xxx_hw_stats, port, data);
} }
int mv88e6xxx_get_sset_count(struct dsa_switch *ds) int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{ {
if (have_sw_in_discards(ds)) struct mv88e6xxx_hw_stat *stat;
return ARRAY_SIZE(mv88e6xxx_hw_stats); int i, j;
return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
stat = &mv88e6xxx_hw_stats[i];
if (mv88e6xxx_has_stat(ds, stat))
j++;
}
return j;
} }
void void
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data) int port, uint64_t *data)
{ {
if (have_sw_in_discards(ds)) struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
_mv88e6xxx_get_ethtool_stats( struct mv88e6xxx_hw_stat *stat;
ds, ARRAY_SIZE(mv88e6xxx_hw_stats), int ret;
mv88e6xxx_hw_stats, port, data); int i, j;
else
_mv88e6xxx_get_ethtool_stats( mutex_lock(&ps->smi_mutex);
ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
mv88e6xxx_hw_stats, port, data); ret = _mv88e6xxx_stats_snapshot(ds, port);
if (ret < 0) {
mutex_unlock(&ps->smi_mutex);
return;
}
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
stat = &mv88e6xxx_hw_stats[i];
if (mv88e6xxx_has_stat(ds, stat)) {
data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);
j++;
}
}
mutex_unlock(&ps->smi_mutex);
} }
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
......
...@@ -288,6 +288,7 @@ ...@@ -288,6 +288,7 @@
#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_BANK_1 BIT(9)
#define GLOBAL_STATS_COUNTER_32 0x1e #define GLOBAL_STATS_COUNTER_32 0x1e
#define GLOBAL_STATS_COUNTER_01 0x1f #define GLOBAL_STATS_COUNTER_01 0x1f
...@@ -420,10 +421,17 @@ struct mv88e6xxx_priv_state { ...@@ -420,10 +421,17 @@ struct mv88e6xxx_priv_state {
struct work_struct bridge_work; struct work_struct bridge_work;
}; };
enum stat_type {
BANK0,
BANK1,
PORT,
};
struct mv88e6xxx_hw_stat { struct mv88e6xxx_hw_stat {
char string[ETH_GSTRING_LEN]; char string[ETH_GSTRING_LEN];
int sizeof_stat; int sizeof_stat;
int reg; int reg;
enum stat_type type;
}; };
int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active); int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment