Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+121 −109
Original line number Original line Diff line number Diff line
@@ -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,14 +731,42 @@ 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,
	struct mv88e6xxx_hw_stat *stat;
	int i, j;

	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
		stat = &mv88e6xxx_hw_stats[i];
		if (mv88e6xxx_has_stat(ds, stat)) {
			memcpy(data + j * ETH_GSTRING_LEN, stat->string,
			       ETH_GSTRING_LEN);
			j++;
		}
	}
}

int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{
	struct mv88e6xxx_hw_stat *stat;
	int i, j;

	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
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
			    int port, uint64_t *data)
			    int port, uint64_t *data)
{
{
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
	struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
	struct mv88e6xxx_hw_stat *stat;
	int ret;
	int ret;
	int i;
	int i, j;


	mutex_lock(&ps->smi_mutex);
	mutex_lock(&ps->smi_mutex);


@@ -733,45 +775,15 @@ static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
		mutex_unlock(&ps->smi_mutex);
		mutex_unlock(&ps->smi_mutex);
		return;
		return;
	}
	}

	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
	/* Read each of the counters. */
		stat = &mv88e6xxx_hw_stats[i];
	for (i = 0; i < nr_stats; i++)
		if (mv88e6xxx_has_stat(ds, stat)) {
		data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
			data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);

			j++;
	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)
{
	if (have_sw_in_discards(ds))
		return ARRAY_SIZE(mv88e6xxx_hw_stats);
	return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
	}
	}


void
	mutex_unlock(&ps->smi_mutex);
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
			    int port, uint64_t *data)
{
	if (have_sw_in_discards(ds))
		_mv88e6xxx_get_ethtool_stats(
			ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
			mv88e6xxx_hw_stats, port, data);
	else
		_mv88e6xxx_get_ethtool_stats(
			ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
			mv88e6xxx_hw_stats, port, data);
}
}


int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
+8 −0
Original line number Original line Diff line number Diff line
@@ -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 {
	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);