Commit 6fff9261 authored by David S. Miller's avatar David S. Miller

Merge branch 'felix-dsa-ethtool-stats'

Vladimir Oltean says:

====================
Standardized ethtool counters for Felix DSA driver

The main purpose of this change set is to add reporting of structured
ethtool statistics counters to the felix DSA driver (see patch 11/14 for
details), as a prerequisite for extending these counters to the
eMAC/pMAC defined by the IEEE MAC Merge layer.

Along the way, the main purpose has diverged into multiple sub-purposes
which are also tackled:

- A bug fix patch submitted to "net" has made ocelot->stats_lock a spin
  lock, which is not an issue currently (all Ocelot switches are MMIO),
  but will be an issue for Colin Foster who is working on a SPI
  controlled Ocelot switch. We restore the hardware access to port stats
  to be sleepable.

- PSFP (tc-gate, tc-police) tc-flower stats on Felix use a non-converged
  procedure to access the hardware counters, although the interface is
  very similar to what is used for the port counters. Benefit from the
  logic used for the port counters, which gains us 64-bit tc-flower
  stats that are resistant to overflows.

- Also export the ndo_get_stats64 method used by the ocelot switchdev
  driver to Felix, so that ifconfig sees something hardware-based as
  well (but not 100% up to date).

- Create a new ocelot_stats.c file which groups everything stats-related
  together. Along with this, also move some other topic-specific code,
  like FDB and PTP, out of the main ocelot.c.

- Minimize the lines of code for the stats layout definitions. These
  changes alone cause the patch set to have an overall reduction of
  lines of code in the driver, even though we are adding new
  functionality as well.

Tested the port counters with lockdep and friends, with some
garden-variety traffic (ping, iperf3) and the PSFP counters with
tools/testing/selftests/drivers/net/ocelot/psfp.sh.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 34df6a8a 4d1d157f
...@@ -1034,6 +1034,55 @@ static void felix_port_qos_map_init(struct ocelot *ocelot, int port) ...@@ -1034,6 +1034,55 @@ static void felix_port_qos_map_init(struct ocelot *ocelot, int port)
} }
} }
static void felix_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_stats64(ocelot, port, stats);
}
static void felix_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_pause_stats(ocelot, port, pause_stats);
}
static void felix_get_rmon_stats(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_rmon_stats(ocelot, port, rmon_stats, ranges);
}
static void felix_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_eth_ctrl_stats(ocelot, port, ctrl_stats);
}
static void felix_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_eth_mac_stats(ocelot, port, mac_stats);
}
static void felix_get_eth_phy_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_phy_stats *phy_stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_eth_phy_stats(ocelot, port, phy_stats);
}
static void felix_get_strings(struct dsa_switch *ds, int port, static void felix_get_strings(struct dsa_switch *ds, int port,
u32 stringset, u8 *data) u32 stringset, u8 *data)
{ {
...@@ -1848,6 +1897,12 @@ const struct dsa_switch_ops felix_switch_ops = { ...@@ -1848,6 +1897,12 @@ const struct dsa_switch_ops felix_switch_ops = {
.setup = felix_setup, .setup = felix_setup,
.teardown = felix_teardown, .teardown = felix_teardown,
.set_ageing_time = felix_set_ageing_time, .set_ageing_time = felix_set_ageing_time,
.get_stats64 = felix_get_stats64,
.get_pause_stats = felix_get_pause_stats,
.get_rmon_stats = felix_get_rmon_stats,
.get_eth_ctrl_stats = felix_get_eth_ctrl_stats,
.get_eth_mac_stats = felix_get_eth_mac_stats,
.get_eth_phy_stats = felix_get_eth_phy_stats,
.get_strings = felix_get_strings, .get_strings = felix_get_strings,
.get_ethtool_stats = felix_get_ethtool_stats, .get_ethtool_stats = felix_get_ethtool_stats,
.get_sset_count = felix_get_sset_count, .get_sset_count = felix_get_sset_count,
......
...@@ -348,7 +348,7 @@ static const u32 vsc9959_sys_regmap[] = { ...@@ -348,7 +348,7 @@ static const u32 vsc9959_sys_regmap[] = {
REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00026c), REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00026c),
REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000270), REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000270),
REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000274), REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000274),
REG(SYS_COUNT_TX_AGING, 0x000278), REG(SYS_COUNT_TX_AGED, 0x000278),
REG(SYS_COUNT_DROP_LOCAL, 0x000400), REG(SYS_COUNT_DROP_LOCAL, 0x000400),
REG(SYS_COUNT_DROP_TAIL, 0x000404), REG(SYS_COUNT_DROP_TAIL, 0x000404),
REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000408), REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000408),
...@@ -367,6 +367,10 @@ static const u32 vsc9959_sys_regmap[] = { ...@@ -367,6 +367,10 @@ static const u32 vsc9959_sys_regmap[] = {
REG(SYS_COUNT_DROP_GREEN_PRIO_5, 0x00043c), REG(SYS_COUNT_DROP_GREEN_PRIO_5, 0x00043c),
REG(SYS_COUNT_DROP_GREEN_PRIO_6, 0x000440), REG(SYS_COUNT_DROP_GREEN_PRIO_6, 0x000440),
REG(SYS_COUNT_DROP_GREEN_PRIO_7, 0x000444), REG(SYS_COUNT_DROP_GREEN_PRIO_7, 0x000444),
REG(SYS_COUNT_SF_MATCHING_FRAMES, 0x000800),
REG(SYS_COUNT_SF_NOT_PASSING_FRAMES, 0x000804),
REG(SYS_COUNT_SF_NOT_PASSING_SDU, 0x000808),
REG(SYS_COUNT_SF_RED_FRAMES, 0x00080c),
REG(SYS_RESET_CFG, 0x000e00), REG(SYS_RESET_CFG, 0x000e00),
REG(SYS_SR_ETYPE_CFG, 0x000e04), REG(SYS_SR_ETYPE_CFG, 0x000e04),
REG(SYS_VLAN_ETYPE_CFG, 0x000e08), REG(SYS_VLAN_ETYPE_CFG, 0x000e08),
...@@ -388,7 +392,6 @@ static const u32 vsc9959_sys_regmap[] = { ...@@ -388,7 +392,6 @@ static const u32 vsc9959_sys_regmap[] = {
REG_RESERVED(SYS_MMGT_FAST), REG_RESERVED(SYS_MMGT_FAST),
REG_RESERVED(SYS_EVENTS_DIF), REG_RESERVED(SYS_EVENTS_DIF),
REG_RESERVED(SYS_EVENTS_CORE), REG_RESERVED(SYS_EVENTS_CORE),
REG(SYS_CNT, 0x000000),
REG(SYS_PTP_STATUS, 0x000f14), REG(SYS_PTP_STATUS, 0x000f14),
REG(SYS_PTP_TXSTAMP, 0x000f18), REG(SYS_PTP_TXSTAMP, 0x000f18),
REG(SYS_PTP_NXT, 0x000f1c), REG(SYS_PTP_NXT, 0x000f1c),
...@@ -620,378 +623,7 @@ static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = { ...@@ -620,378 +623,7 @@ static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = {
}; };
static const struct ocelot_stat_layout vsc9959_stats_layout[OCELOT_NUM_STATS] = { static const struct ocelot_stat_layout vsc9959_stats_layout[OCELOT_NUM_STATS] = {
[OCELOT_STAT_RX_OCTETS] = { OCELOT_COMMON_STATS,
.name = "rx_octets",
.reg = SYS_COUNT_RX_OCTETS,
},
[OCELOT_STAT_RX_UNICAST] = {
.name = "rx_unicast",
.reg = SYS_COUNT_RX_UNICAST,
},
[OCELOT_STAT_RX_MULTICAST] = {
.name = "rx_multicast",
.reg = SYS_COUNT_RX_MULTICAST,
},
[OCELOT_STAT_RX_BROADCAST] = {
.name = "rx_broadcast",
.reg = SYS_COUNT_RX_BROADCAST,
},
[OCELOT_STAT_RX_SHORTS] = {
.name = "rx_shorts",
.reg = SYS_COUNT_RX_SHORTS,
},
[OCELOT_STAT_RX_FRAGMENTS] = {
.name = "rx_fragments",
.reg = SYS_COUNT_RX_FRAGMENTS,
},
[OCELOT_STAT_RX_JABBERS] = {
.name = "rx_jabbers",
.reg = SYS_COUNT_RX_JABBERS,
},
[OCELOT_STAT_RX_CRC_ALIGN_ERRS] = {
.name = "rx_crc_align_errs",
.reg = SYS_COUNT_RX_CRC_ALIGN_ERRS,
},
[OCELOT_STAT_RX_SYM_ERRS] = {
.name = "rx_sym_errs",
.reg = SYS_COUNT_RX_SYM_ERRS,
},
[OCELOT_STAT_RX_64] = {
.name = "rx_frames_below_65_octets",
.reg = SYS_COUNT_RX_64,
},
[OCELOT_STAT_RX_65_127] = {
.name = "rx_frames_65_to_127_octets",
.reg = SYS_COUNT_RX_65_127,
},
[OCELOT_STAT_RX_128_255] = {
.name = "rx_frames_128_to_255_octets",
.reg = SYS_COUNT_RX_128_255,
},
[OCELOT_STAT_RX_256_511] = {
.name = "rx_frames_256_to_511_octets",
.reg = SYS_COUNT_RX_256_511,
},
[OCELOT_STAT_RX_512_1023] = {
.name = "rx_frames_512_to_1023_octets",
.reg = SYS_COUNT_RX_512_1023,
},
[OCELOT_STAT_RX_1024_1526] = {
.name = "rx_frames_1024_to_1526_octets",
.reg = SYS_COUNT_RX_1024_1526,
},
[OCELOT_STAT_RX_1527_MAX] = {
.name = "rx_frames_over_1526_octets",
.reg = SYS_COUNT_RX_1527_MAX,
},
[OCELOT_STAT_RX_PAUSE] = {
.name = "rx_pause",
.reg = SYS_COUNT_RX_PAUSE,
},
[OCELOT_STAT_RX_CONTROL] = {
.name = "rx_control",
.reg = SYS_COUNT_RX_CONTROL,
},
[OCELOT_STAT_RX_LONGS] = {
.name = "rx_longs",
.reg = SYS_COUNT_RX_LONGS,
},
[OCELOT_STAT_RX_CLASSIFIED_DROPS] = {
.name = "rx_classified_drops",
.reg = SYS_COUNT_RX_CLASSIFIED_DROPS,
},
[OCELOT_STAT_RX_RED_PRIO_0] = {
.name = "rx_red_prio_0",
.reg = SYS_COUNT_RX_RED_PRIO_0,
},
[OCELOT_STAT_RX_RED_PRIO_1] = {
.name = "rx_red_prio_1",
.reg = SYS_COUNT_RX_RED_PRIO_1,
},
[OCELOT_STAT_RX_RED_PRIO_2] = {
.name = "rx_red_prio_2",
.reg = SYS_COUNT_RX_RED_PRIO_2,
},
[OCELOT_STAT_RX_RED_PRIO_3] = {
.name = "rx_red_prio_3",
.reg = SYS_COUNT_RX_RED_PRIO_3,
},
[OCELOT_STAT_RX_RED_PRIO_4] = {
.name = "rx_red_prio_4",
.reg = SYS_COUNT_RX_RED_PRIO_4,
},
[OCELOT_STAT_RX_RED_PRIO_5] = {
.name = "rx_red_prio_5",
.reg = SYS_COUNT_RX_RED_PRIO_5,
},
[OCELOT_STAT_RX_RED_PRIO_6] = {
.name = "rx_red_prio_6",
.reg = SYS_COUNT_RX_RED_PRIO_6,
},
[OCELOT_STAT_RX_RED_PRIO_7] = {
.name = "rx_red_prio_7",
.reg = SYS_COUNT_RX_RED_PRIO_7,
},
[OCELOT_STAT_RX_YELLOW_PRIO_0] = {
.name = "rx_yellow_prio_0",
.reg = SYS_COUNT_RX_YELLOW_PRIO_0,
},
[OCELOT_STAT_RX_YELLOW_PRIO_1] = {
.name = "rx_yellow_prio_1",
.reg = SYS_COUNT_RX_YELLOW_PRIO_1,
},
[OCELOT_STAT_RX_YELLOW_PRIO_2] = {
.name = "rx_yellow_prio_2",
.reg = SYS_COUNT_RX_YELLOW_PRIO_2,
},
[OCELOT_STAT_RX_YELLOW_PRIO_3] = {
.name = "rx_yellow_prio_3",
.reg = SYS_COUNT_RX_YELLOW_PRIO_3,
},
[OCELOT_STAT_RX_YELLOW_PRIO_4] = {
.name = "rx_yellow_prio_4",
.reg = SYS_COUNT_RX_YELLOW_PRIO_4,
},
[OCELOT_STAT_RX_YELLOW_PRIO_5] = {
.name = "rx_yellow_prio_5",
.reg = SYS_COUNT_RX_YELLOW_PRIO_5,
},
[OCELOT_STAT_RX_YELLOW_PRIO_6] = {
.name = "rx_yellow_prio_6",
.reg = SYS_COUNT_RX_YELLOW_PRIO_6,
},
[OCELOT_STAT_RX_YELLOW_PRIO_7] = {
.name = "rx_yellow_prio_7",
.reg = SYS_COUNT_RX_YELLOW_PRIO_7,
},
[OCELOT_STAT_RX_GREEN_PRIO_0] = {
.name = "rx_green_prio_0",
.reg = SYS_COUNT_RX_GREEN_PRIO_0,
},
[OCELOT_STAT_RX_GREEN_PRIO_1] = {
.name = "rx_green_prio_1",
.reg = SYS_COUNT_RX_GREEN_PRIO_1,
},
[OCELOT_STAT_RX_GREEN_PRIO_2] = {
.name = "rx_green_prio_2",
.reg = SYS_COUNT_RX_GREEN_PRIO_2,
},
[OCELOT_STAT_RX_GREEN_PRIO_3] = {
.name = "rx_green_prio_3",
.reg = SYS_COUNT_RX_GREEN_PRIO_3,
},
[OCELOT_STAT_RX_GREEN_PRIO_4] = {
.name = "rx_green_prio_4",
.reg = SYS_COUNT_RX_GREEN_PRIO_4,
},
[OCELOT_STAT_RX_GREEN_PRIO_5] = {
.name = "rx_green_prio_5",
.reg = SYS_COUNT_RX_GREEN_PRIO_5,
},
[OCELOT_STAT_RX_GREEN_PRIO_6] = {
.name = "rx_green_prio_6",
.reg = SYS_COUNT_RX_GREEN_PRIO_6,
},
[OCELOT_STAT_RX_GREEN_PRIO_7] = {
.name = "rx_green_prio_7",
.reg = SYS_COUNT_RX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_OCTETS] = {
.name = "tx_octets",
.reg = SYS_COUNT_TX_OCTETS,
},
[OCELOT_STAT_TX_UNICAST] = {
.name = "tx_unicast",
.reg = SYS_COUNT_TX_UNICAST,
},
[OCELOT_STAT_TX_MULTICAST] = {
.name = "tx_multicast",
.reg = SYS_COUNT_TX_MULTICAST,
},
[OCELOT_STAT_TX_BROADCAST] = {
.name = "tx_broadcast",
.reg = SYS_COUNT_TX_BROADCAST,
},
[OCELOT_STAT_TX_COLLISION] = {
.name = "tx_collision",
.reg = SYS_COUNT_TX_COLLISION,
},
[OCELOT_STAT_TX_DROPS] = {
.name = "tx_drops",
.reg = SYS_COUNT_TX_DROPS,
},
[OCELOT_STAT_TX_PAUSE] = {
.name = "tx_pause",
.reg = SYS_COUNT_TX_PAUSE,
},
[OCELOT_STAT_TX_64] = {
.name = "tx_frames_below_65_octets",
.reg = SYS_COUNT_TX_64,
},
[OCELOT_STAT_TX_65_127] = {
.name = "tx_frames_65_to_127_octets",
.reg = SYS_COUNT_TX_65_127,
},
[OCELOT_STAT_TX_128_255] = {
.name = "tx_frames_128_255_octets",
.reg = SYS_COUNT_TX_128_255,
},
[OCELOT_STAT_TX_256_511] = {
.name = "tx_frames_256_511_octets",
.reg = SYS_COUNT_TX_256_511,
},
[OCELOT_STAT_TX_512_1023] = {
.name = "tx_frames_512_1023_octets",
.reg = SYS_COUNT_TX_512_1023,
},
[OCELOT_STAT_TX_1024_1526] = {
.name = "tx_frames_1024_1526_octets",
.reg = SYS_COUNT_TX_1024_1526,
},
[OCELOT_STAT_TX_1527_MAX] = {
.name = "tx_frames_over_1526_octets",
.reg = SYS_COUNT_TX_1527_MAX,
},
[OCELOT_STAT_TX_YELLOW_PRIO_0] = {
.name = "tx_yellow_prio_0",
.reg = SYS_COUNT_TX_YELLOW_PRIO_0,
},
[OCELOT_STAT_TX_YELLOW_PRIO_1] = {
.name = "tx_yellow_prio_1",
.reg = SYS_COUNT_TX_YELLOW_PRIO_1,
},
[OCELOT_STAT_TX_YELLOW_PRIO_2] = {
.name = "tx_yellow_prio_2",
.reg = SYS_COUNT_TX_YELLOW_PRIO_2,
},
[OCELOT_STAT_TX_YELLOW_PRIO_3] = {
.name = "tx_yellow_prio_3",
.reg = SYS_COUNT_TX_YELLOW_PRIO_3,
},
[OCELOT_STAT_TX_YELLOW_PRIO_4] = {
.name = "tx_yellow_prio_4",
.reg = SYS_COUNT_TX_YELLOW_PRIO_4,
},
[OCELOT_STAT_TX_YELLOW_PRIO_5] = {
.name = "tx_yellow_prio_5",
.reg = SYS_COUNT_TX_YELLOW_PRIO_5,
},
[OCELOT_STAT_TX_YELLOW_PRIO_6] = {
.name = "tx_yellow_prio_6",
.reg = SYS_COUNT_TX_YELLOW_PRIO_6,
},
[OCELOT_STAT_TX_YELLOW_PRIO_7] = {
.name = "tx_yellow_prio_7",
.reg = SYS_COUNT_TX_YELLOW_PRIO_7,
},
[OCELOT_STAT_TX_GREEN_PRIO_0] = {
.name = "tx_green_prio_0",
.reg = SYS_COUNT_TX_GREEN_PRIO_0,
},
[OCELOT_STAT_TX_GREEN_PRIO_1] = {
.name = "tx_green_prio_1",
.reg = SYS_COUNT_TX_GREEN_PRIO_1,
},
[OCELOT_STAT_TX_GREEN_PRIO_2] = {
.name = "tx_green_prio_2",
.reg = SYS_COUNT_TX_GREEN_PRIO_2,
},
[OCELOT_STAT_TX_GREEN_PRIO_3] = {
.name = "tx_green_prio_3",
.reg = SYS_COUNT_TX_GREEN_PRIO_3,
},
[OCELOT_STAT_TX_GREEN_PRIO_4] = {
.name = "tx_green_prio_4",
.reg = SYS_COUNT_TX_GREEN_PRIO_4,
},
[OCELOT_STAT_TX_GREEN_PRIO_5] = {
.name = "tx_green_prio_5",
.reg = SYS_COUNT_TX_GREEN_PRIO_5,
},
[OCELOT_STAT_TX_GREEN_PRIO_6] = {
.name = "tx_green_prio_6",
.reg = SYS_COUNT_TX_GREEN_PRIO_6,
},
[OCELOT_STAT_TX_GREEN_PRIO_7] = {
.name = "tx_green_prio_7",
.reg = SYS_COUNT_TX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_AGED] = {
.name = "tx_aged",
.reg = SYS_COUNT_TX_AGING,
},
[OCELOT_STAT_DROP_LOCAL] = {
.name = "drop_local",
.reg = SYS_COUNT_DROP_LOCAL,
},
[OCELOT_STAT_DROP_TAIL] = {
.name = "drop_tail",
.reg = SYS_COUNT_DROP_TAIL,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_0] = {
.name = "drop_yellow_prio_0",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_0,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_1] = {
.name = "drop_yellow_prio_1",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_1,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_2] = {
.name = "drop_yellow_prio_2",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_2,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_3] = {
.name = "drop_yellow_prio_3",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_3,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_4] = {
.name = "drop_yellow_prio_4",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_4,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_5] = {
.name = "drop_yellow_prio_5",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_5,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_6] = {
.name = "drop_yellow_prio_6",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_6,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_7] = {
.name = "drop_yellow_prio_7",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_7,
},
[OCELOT_STAT_DROP_GREEN_PRIO_0] = {
.name = "drop_green_prio_0",
.reg = SYS_COUNT_DROP_GREEN_PRIO_0,
},
[OCELOT_STAT_DROP_GREEN_PRIO_1] = {
.name = "drop_green_prio_1",
.reg = SYS_COUNT_DROP_GREEN_PRIO_1,
},
[OCELOT_STAT_DROP_GREEN_PRIO_2] = {
.name = "drop_green_prio_2",
.reg = SYS_COUNT_DROP_GREEN_PRIO_2,
},
[OCELOT_STAT_DROP_GREEN_PRIO_3] = {
.name = "drop_green_prio_3",
.reg = SYS_COUNT_DROP_GREEN_PRIO_3,
},
[OCELOT_STAT_DROP_GREEN_PRIO_4] = {
.name = "drop_green_prio_4",
.reg = SYS_COUNT_DROP_GREEN_PRIO_4,
},
[OCELOT_STAT_DROP_GREEN_PRIO_5] = {
.name = "drop_green_prio_5",
.reg = SYS_COUNT_DROP_GREEN_PRIO_5,
},
[OCELOT_STAT_DROP_GREEN_PRIO_6] = {
.name = "drop_green_prio_6",
.reg = SYS_COUNT_DROP_GREEN_PRIO_6,
},
[OCELOT_STAT_DROP_GREEN_PRIO_7] = {
.name = "drop_green_prio_7",
.reg = SYS_COUNT_DROP_GREEN_PRIO_7,
},
}; };
static const struct vcap_field vsc9959_vcap_es0_keys[] = { static const struct vcap_field vsc9959_vcap_es0_keys[] = {
...@@ -2043,7 +1675,15 @@ struct felix_stream { ...@@ -2043,7 +1675,15 @@ struct felix_stream {
u32 ssid; u32 ssid;
}; };
struct felix_stream_filter_counters {
u64 match;
u64 not_pass_gate;
u64 not_pass_sdu;
u64 red;
};
struct felix_stream_filter { struct felix_stream_filter {
struct felix_stream_filter_counters stats;
struct list_head list; struct list_head list;
refcount_t refcount; refcount_t refcount;
u32 index; u32 index;
...@@ -2058,13 +1698,6 @@ struct felix_stream_filter { ...@@ -2058,13 +1698,6 @@ struct felix_stream_filter {
u32 maxsdu; u32 maxsdu;
}; };
struct felix_stream_filter_counters {
u32 match;
u32 not_pass_gate;
u32 not_pass_sdu;
u32 red;
};
struct felix_stream_gate { struct felix_stream_gate {
u32 index; u32 index;
u8 enable; u8 enable;
...@@ -2568,29 +2201,6 @@ static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot, ...@@ -2568,29 +2201,6 @@ static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot,
} }
} }
static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index,
struct felix_stream_filter_counters *counters)
{
spin_lock(&ocelot->stats_lock);
ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(index),
SYS_STAT_CFG_STAT_VIEW_M,
SYS_STAT_CFG);
counters->match = ocelot_read_gix(ocelot, SYS_CNT, 0x200);
counters->not_pass_gate = ocelot_read_gix(ocelot, SYS_CNT, 0x201);
counters->not_pass_sdu = ocelot_read_gix(ocelot, SYS_CNT, 0x202);
counters->red = ocelot_read_gix(ocelot, SYS_CNT, 0x203);
/* Clear the PSFP counter. */
ocelot_write(ocelot,
SYS_STAT_CFG_STAT_VIEW(index) |
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10),
SYS_STAT_CFG);
spin_unlock(&ocelot->stats_lock);
}
static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
struct flow_cls_offload *f) struct flow_cls_offload *f)
{ {
...@@ -2615,6 +2225,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, ...@@ -2615,6 +2225,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
return ret; return ret;
} }
mutex_lock(&psfp->lock);
flow_action_for_each(i, a, &f->rule->action) { flow_action_for_each(i, a, &f->rule->action) {
switch (a->id) { switch (a->id) {
case FLOW_ACTION_GATE: case FLOW_ACTION_GATE:
...@@ -2656,6 +2268,7 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, ...@@ -2656,6 +2268,7 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
sfi.maxsdu = a->police.mtu; sfi.maxsdu = a->police.mtu;
break; break;
default: default:
mutex_unlock(&psfp->lock);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
} }
...@@ -2725,6 +2338,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, ...@@ -2725,6 +2338,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
goto err; goto err;
} }
mutex_unlock(&psfp->lock);
return 0; return 0;
err: err:
...@@ -2734,6 +2349,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, ...@@ -2734,6 +2349,8 @@ static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
if (sfi.fm_valid) if (sfi.fm_valid)
ocelot_vcap_policer_del(ocelot, sfi.fmid); ocelot_vcap_policer_del(ocelot, sfi.fmid);
mutex_unlock(&psfp->lock);
return ret; return ret;
} }
...@@ -2741,18 +2358,22 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot, ...@@ -2741,18 +2358,22 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot,
struct flow_cls_offload *f) struct flow_cls_offload *f)
{ {
struct felix_stream *stream, tmp, *stream_entry; struct felix_stream *stream, tmp, *stream_entry;
struct ocelot_psfp_list *psfp = &ocelot->psfp;
static struct felix_stream_filter *sfi; static struct felix_stream_filter *sfi;
struct ocelot_psfp_list *psfp;
psfp = &ocelot->psfp; mutex_lock(&psfp->lock);
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie); stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
if (!stream) if (!stream) {
mutex_unlock(&psfp->lock);
return -ENOMEM; return -ENOMEM;
}
sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid); sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
if (!sfi) if (!sfi) {
mutex_unlock(&psfp->lock);
return -ENOMEM; return -ENOMEM;
}
if (sfi->sg_valid) if (sfi->sg_valid)
vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid); vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid);
...@@ -2778,27 +2399,83 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot, ...@@ -2778,27 +2399,83 @@ static int vsc9959_psfp_filter_del(struct ocelot *ocelot,
stream_entry->ports); stream_entry->ports);
} }
mutex_unlock(&psfp->lock);
return 0; return 0;
} }
static void vsc9959_update_sfid_stats(struct ocelot *ocelot,
struct felix_stream_filter *sfi)
{
struct felix_stream_filter_counters *s = &sfi->stats;
u32 match, not_pass_gate, not_pass_sdu, red;
u32 sfid = sfi->index;
lockdep_assert_held(&ocelot->stat_view_lock);
ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(sfid),
SYS_STAT_CFG_STAT_VIEW_M,
SYS_STAT_CFG);
match = ocelot_read(ocelot, SYS_COUNT_SF_MATCHING_FRAMES);
not_pass_gate = ocelot_read(ocelot, SYS_COUNT_SF_NOT_PASSING_FRAMES);
not_pass_sdu = ocelot_read(ocelot, SYS_COUNT_SF_NOT_PASSING_SDU);
red = ocelot_read(ocelot, SYS_COUNT_SF_RED_FRAMES);
/* Clear the PSFP counter. */
ocelot_write(ocelot,
SYS_STAT_CFG_STAT_VIEW(sfid) |
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10),
SYS_STAT_CFG);
s->match += match;
s->not_pass_gate += not_pass_gate;
s->not_pass_sdu += not_pass_sdu;
s->red += red;
}
/* Caller must hold &ocelot->stat_view_lock */
static void vsc9959_update_stats(struct ocelot *ocelot)
{
struct ocelot_psfp_list *psfp = &ocelot->psfp;
struct felix_stream_filter *sfi;
mutex_lock(&psfp->lock);
list_for_each_entry(sfi, &psfp->sfi_list, list)
vsc9959_update_sfid_stats(ocelot, sfi);
mutex_unlock(&psfp->lock);
}
static int vsc9959_psfp_stats_get(struct ocelot *ocelot, static int vsc9959_psfp_stats_get(struct ocelot *ocelot,
struct flow_cls_offload *f, struct flow_cls_offload *f,
struct flow_stats *stats) struct flow_stats *stats)
{ {
struct felix_stream_filter_counters counters; struct ocelot_psfp_list *psfp = &ocelot->psfp;
struct ocelot_psfp_list *psfp; struct felix_stream_filter_counters *s;
static struct felix_stream_filter *sfi;
struct felix_stream *stream; struct felix_stream *stream;
psfp = &ocelot->psfp;
stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie); stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
if (!stream) if (!stream)
return -ENOMEM; return -ENOMEM;
vsc9959_psfp_counters_get(ocelot, stream->sfid, &counters); sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
if (!sfi)
return -EINVAL;
mutex_lock(&ocelot->stat_view_lock);
vsc9959_update_sfid_stats(ocelot, sfi);
s = &sfi->stats;
stats->pkts = s->match;
stats->drops = s->not_pass_gate + s->not_pass_sdu + s->red;
memset(s, 0, sizeof(*s));
stats->pkts = counters.match; mutex_unlock(&ocelot->stat_view_lock);
stats->drops = counters.not_pass_gate + counters.not_pass_sdu +
counters.red;
return 0; return 0;
} }
...@@ -2810,6 +2487,7 @@ static void vsc9959_psfp_init(struct ocelot *ocelot) ...@@ -2810,6 +2487,7 @@ static void vsc9959_psfp_init(struct ocelot *ocelot)
INIT_LIST_HEAD(&psfp->stream_list); INIT_LIST_HEAD(&psfp->stream_list);
INIT_LIST_HEAD(&psfp->sfi_list); INIT_LIST_HEAD(&psfp->sfi_list);
INIT_LIST_HEAD(&psfp->sgi_list); INIT_LIST_HEAD(&psfp->sgi_list);
mutex_init(&psfp->lock);
} }
/* When using cut-through forwarding and the egress port runs at a higher data /* When using cut-through forwarding and the egress port runs at a higher data
...@@ -2908,6 +2586,7 @@ static const struct ocelot_ops vsc9959_ops = { ...@@ -2908,6 +2586,7 @@ static const struct ocelot_ops vsc9959_ops = {
.psfp_stats_get = vsc9959_psfp_stats_get, .psfp_stats_get = vsc9959_psfp_stats_get,
.cut_through_fwd = vsc9959_cut_through_fwd, .cut_through_fwd = vsc9959_cut_through_fwd,
.tas_clock_adjust = vsc9959_tas_clock_adjust, .tas_clock_adjust = vsc9959_tas_clock_adjust,
.update_stats = vsc9959_update_stats,
}; };
static const struct felix_info felix_info_vsc9959 = { static const struct felix_info felix_info_vsc9959 = {
......
...@@ -343,7 +343,7 @@ static const u32 vsc9953_sys_regmap[] = { ...@@ -343,7 +343,7 @@ static const u32 vsc9953_sys_regmap[] = {
REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00016c), REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00016c),
REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000170), REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000170),
REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000174), REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000174),
REG(SYS_COUNT_TX_AGING, 0x000178), REG(SYS_COUNT_TX_AGED, 0x000178),
REG(SYS_COUNT_DROP_LOCAL, 0x000200), REG(SYS_COUNT_DROP_LOCAL, 0x000200),
REG(SYS_COUNT_DROP_TAIL, 0x000204), REG(SYS_COUNT_DROP_TAIL, 0x000204),
REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000208), REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000208),
...@@ -383,7 +383,6 @@ static const u32 vsc9953_sys_regmap[] = { ...@@ -383,7 +383,6 @@ static const u32 vsc9953_sys_regmap[] = {
REG_RESERVED(SYS_MMGT_FAST), REG_RESERVED(SYS_MMGT_FAST),
REG_RESERVED(SYS_EVENTS_DIF), REG_RESERVED(SYS_EVENTS_DIF),
REG_RESERVED(SYS_EVENTS_CORE), REG_RESERVED(SYS_EVENTS_CORE),
REG_RESERVED(SYS_CNT),
REG_RESERVED(SYS_PTP_STATUS), REG_RESERVED(SYS_PTP_STATUS),
REG_RESERVED(SYS_PTP_TXSTAMP), REG_RESERVED(SYS_PTP_TXSTAMP),
REG_RESERVED(SYS_PTP_NXT), REG_RESERVED(SYS_PTP_NXT),
...@@ -615,378 +614,7 @@ static const struct reg_field vsc9953_regfields[REGFIELD_MAX] = { ...@@ -615,378 +614,7 @@ static const struct reg_field vsc9953_regfields[REGFIELD_MAX] = {
}; };
static const struct ocelot_stat_layout vsc9953_stats_layout[OCELOT_NUM_STATS] = { static const struct ocelot_stat_layout vsc9953_stats_layout[OCELOT_NUM_STATS] = {
[OCELOT_STAT_RX_OCTETS] = { OCELOT_COMMON_STATS,
.name = "rx_octets",
.reg = SYS_COUNT_RX_OCTETS,
},
[OCELOT_STAT_RX_UNICAST] = {
.name = "rx_unicast",
.reg = SYS_COUNT_RX_UNICAST,
},
[OCELOT_STAT_RX_MULTICAST] = {
.name = "rx_multicast",
.reg = SYS_COUNT_RX_MULTICAST,
},
[OCELOT_STAT_RX_BROADCAST] = {
.name = "rx_broadcast",
.reg = SYS_COUNT_RX_BROADCAST,
},
[OCELOT_STAT_RX_SHORTS] = {
.name = "rx_shorts",
.reg = SYS_COUNT_RX_SHORTS,
},
[OCELOT_STAT_RX_FRAGMENTS] = {
.name = "rx_fragments",
.reg = SYS_COUNT_RX_FRAGMENTS,
},
[OCELOT_STAT_RX_JABBERS] = {
.name = "rx_jabbers",
.reg = SYS_COUNT_RX_JABBERS,
},
[OCELOT_STAT_RX_CRC_ALIGN_ERRS] = {
.name = "rx_crc_align_errs",
.reg = SYS_COUNT_RX_CRC_ALIGN_ERRS,
},
[OCELOT_STAT_RX_SYM_ERRS] = {
.name = "rx_sym_errs",
.reg = SYS_COUNT_RX_SYM_ERRS,
},
[OCELOT_STAT_RX_64] = {
.name = "rx_frames_below_65_octets",
.reg = SYS_COUNT_RX_64,
},
[OCELOT_STAT_RX_65_127] = {
.name = "rx_frames_65_to_127_octets",
.reg = SYS_COUNT_RX_65_127,
},
[OCELOT_STAT_RX_128_255] = {
.name = "rx_frames_128_to_255_octets",
.reg = SYS_COUNT_RX_128_255,
},
[OCELOT_STAT_RX_256_511] = {
.name = "rx_frames_256_to_511_octets",
.reg = SYS_COUNT_RX_256_511,
},
[OCELOT_STAT_RX_512_1023] = {
.name = "rx_frames_512_to_1023_octets",
.reg = SYS_COUNT_RX_512_1023,
},
[OCELOT_STAT_RX_1024_1526] = {
.name = "rx_frames_1024_to_1526_octets",
.reg = SYS_COUNT_RX_1024_1526,
},
[OCELOT_STAT_RX_1527_MAX] = {
.name = "rx_frames_over_1526_octets",
.reg = SYS_COUNT_RX_1527_MAX,
},
[OCELOT_STAT_RX_PAUSE] = {
.name = "rx_pause",
.reg = SYS_COUNT_RX_PAUSE,
},
[OCELOT_STAT_RX_CONTROL] = {
.name = "rx_control",
.reg = SYS_COUNT_RX_CONTROL,
},
[OCELOT_STAT_RX_LONGS] = {
.name = "rx_longs",
.reg = SYS_COUNT_RX_LONGS,
},
[OCELOT_STAT_RX_CLASSIFIED_DROPS] = {
.name = "rx_classified_drops",
.reg = SYS_COUNT_RX_CLASSIFIED_DROPS,
},
[OCELOT_STAT_RX_RED_PRIO_0] = {
.name = "rx_red_prio_0",
.reg = SYS_COUNT_RX_RED_PRIO_0,
},
[OCELOT_STAT_RX_RED_PRIO_1] = {
.name = "rx_red_prio_1",
.reg = SYS_COUNT_RX_RED_PRIO_1,
},
[OCELOT_STAT_RX_RED_PRIO_2] = {
.name = "rx_red_prio_2",
.reg = SYS_COUNT_RX_RED_PRIO_2,
},
[OCELOT_STAT_RX_RED_PRIO_3] = {
.name = "rx_red_prio_3",
.reg = SYS_COUNT_RX_RED_PRIO_3,
},
[OCELOT_STAT_RX_RED_PRIO_4] = {
.name = "rx_red_prio_4",
.reg = SYS_COUNT_RX_RED_PRIO_4,
},
[OCELOT_STAT_RX_RED_PRIO_5] = {
.name = "rx_red_prio_5",
.reg = SYS_COUNT_RX_RED_PRIO_5,
},
[OCELOT_STAT_RX_RED_PRIO_6] = {
.name = "rx_red_prio_6",
.reg = SYS_COUNT_RX_RED_PRIO_6,
},
[OCELOT_STAT_RX_RED_PRIO_7] = {
.name = "rx_red_prio_7",
.reg = SYS_COUNT_RX_RED_PRIO_7,
},
[OCELOT_STAT_RX_YELLOW_PRIO_0] = {
.name = "rx_yellow_prio_0",
.reg = SYS_COUNT_RX_YELLOW_PRIO_0,
},
[OCELOT_STAT_RX_YELLOW_PRIO_1] = {
.name = "rx_yellow_prio_1",
.reg = SYS_COUNT_RX_YELLOW_PRIO_1,
},
[OCELOT_STAT_RX_YELLOW_PRIO_2] = {
.name = "rx_yellow_prio_2",
.reg = SYS_COUNT_RX_YELLOW_PRIO_2,
},
[OCELOT_STAT_RX_YELLOW_PRIO_3] = {
.name = "rx_yellow_prio_3",
.reg = SYS_COUNT_RX_YELLOW_PRIO_3,
},
[OCELOT_STAT_RX_YELLOW_PRIO_4] = {
.name = "rx_yellow_prio_4",
.reg = SYS_COUNT_RX_YELLOW_PRIO_4,
},
[OCELOT_STAT_RX_YELLOW_PRIO_5] = {
.name = "rx_yellow_prio_5",
.reg = SYS_COUNT_RX_YELLOW_PRIO_5,
},
[OCELOT_STAT_RX_YELLOW_PRIO_6] = {
.name = "rx_yellow_prio_6",
.reg = SYS_COUNT_RX_YELLOW_PRIO_6,
},
[OCELOT_STAT_RX_YELLOW_PRIO_7] = {
.name = "rx_yellow_prio_7",
.reg = SYS_COUNT_RX_YELLOW_PRIO_7,
},
[OCELOT_STAT_RX_GREEN_PRIO_0] = {
.name = "rx_green_prio_0",
.reg = SYS_COUNT_RX_GREEN_PRIO_0,
},
[OCELOT_STAT_RX_GREEN_PRIO_1] = {
.name = "rx_green_prio_1",
.reg = SYS_COUNT_RX_GREEN_PRIO_1,
},
[OCELOT_STAT_RX_GREEN_PRIO_2] = {
.name = "rx_green_prio_2",
.reg = SYS_COUNT_RX_GREEN_PRIO_2,
},
[OCELOT_STAT_RX_GREEN_PRIO_3] = {
.name = "rx_green_prio_3",
.reg = SYS_COUNT_RX_GREEN_PRIO_3,
},
[OCELOT_STAT_RX_GREEN_PRIO_4] = {
.name = "rx_green_prio_4",
.reg = SYS_COUNT_RX_GREEN_PRIO_4,
},
[OCELOT_STAT_RX_GREEN_PRIO_5] = {
.name = "rx_green_prio_5",
.reg = SYS_COUNT_RX_GREEN_PRIO_5,
},
[OCELOT_STAT_RX_GREEN_PRIO_6] = {
.name = "rx_green_prio_6",
.reg = SYS_COUNT_RX_GREEN_PRIO_6,
},
[OCELOT_STAT_RX_GREEN_PRIO_7] = {
.name = "rx_green_prio_7",
.reg = SYS_COUNT_RX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_OCTETS] = {
.name = "tx_octets",
.reg = SYS_COUNT_TX_OCTETS,
},
[OCELOT_STAT_TX_UNICAST] = {
.name = "tx_unicast",
.reg = SYS_COUNT_TX_UNICAST,
},
[OCELOT_STAT_TX_MULTICAST] = {
.name = "tx_multicast",
.reg = SYS_COUNT_TX_MULTICAST,
},
[OCELOT_STAT_TX_BROADCAST] = {
.name = "tx_broadcast",
.reg = SYS_COUNT_TX_BROADCAST,
},
[OCELOT_STAT_TX_COLLISION] = {
.name = "tx_collision",
.reg = SYS_COUNT_TX_COLLISION,
},
[OCELOT_STAT_TX_DROPS] = {
.name = "tx_drops",
.reg = SYS_COUNT_TX_DROPS,
},
[OCELOT_STAT_TX_PAUSE] = {
.name = "tx_pause",
.reg = SYS_COUNT_TX_PAUSE,
},
[OCELOT_STAT_TX_64] = {
.name = "tx_frames_below_65_octets",
.reg = SYS_COUNT_TX_64,
},
[OCELOT_STAT_TX_65_127] = {
.name = "tx_frames_65_to_127_octets",
.reg = SYS_COUNT_TX_65_127,
},
[OCELOT_STAT_TX_128_255] = {
.name = "tx_frames_128_255_octets",
.reg = SYS_COUNT_TX_128_255,
},
[OCELOT_STAT_TX_256_511] = {
.name = "tx_frames_256_511_octets",
.reg = SYS_COUNT_TX_256_511,
},
[OCELOT_STAT_TX_512_1023] = {
.name = "tx_frames_512_1023_octets",
.reg = SYS_COUNT_TX_512_1023,
},
[OCELOT_STAT_TX_1024_1526] = {
.name = "tx_frames_1024_1526_octets",
.reg = SYS_COUNT_TX_1024_1526,
},
[OCELOT_STAT_TX_1527_MAX] = {
.name = "tx_frames_over_1526_octets",
.reg = SYS_COUNT_TX_1527_MAX,
},
[OCELOT_STAT_TX_YELLOW_PRIO_0] = {
.name = "tx_yellow_prio_0",
.reg = SYS_COUNT_TX_YELLOW_PRIO_0,
},
[OCELOT_STAT_TX_YELLOW_PRIO_1] = {
.name = "tx_yellow_prio_1",
.reg = SYS_COUNT_TX_YELLOW_PRIO_1,
},
[OCELOT_STAT_TX_YELLOW_PRIO_2] = {
.name = "tx_yellow_prio_2",
.reg = SYS_COUNT_TX_YELLOW_PRIO_2,
},
[OCELOT_STAT_TX_YELLOW_PRIO_3] = {
.name = "tx_yellow_prio_3",
.reg = SYS_COUNT_TX_YELLOW_PRIO_3,
},
[OCELOT_STAT_TX_YELLOW_PRIO_4] = {
.name = "tx_yellow_prio_4",
.reg = SYS_COUNT_TX_YELLOW_PRIO_4,
},
[OCELOT_STAT_TX_YELLOW_PRIO_5] = {
.name = "tx_yellow_prio_5",
.reg = SYS_COUNT_TX_YELLOW_PRIO_5,
},
[OCELOT_STAT_TX_YELLOW_PRIO_6] = {
.name = "tx_yellow_prio_6",
.reg = SYS_COUNT_TX_YELLOW_PRIO_6,
},
[OCELOT_STAT_TX_YELLOW_PRIO_7] = {
.name = "tx_yellow_prio_7",
.reg = SYS_COUNT_TX_YELLOW_PRIO_7,
},
[OCELOT_STAT_TX_GREEN_PRIO_0] = {
.name = "tx_green_prio_0",
.reg = SYS_COUNT_TX_GREEN_PRIO_0,
},
[OCELOT_STAT_TX_GREEN_PRIO_1] = {
.name = "tx_green_prio_1",
.reg = SYS_COUNT_TX_GREEN_PRIO_1,
},
[OCELOT_STAT_TX_GREEN_PRIO_2] = {
.name = "tx_green_prio_2",
.reg = SYS_COUNT_TX_GREEN_PRIO_2,
},
[OCELOT_STAT_TX_GREEN_PRIO_3] = {
.name = "tx_green_prio_3",
.reg = SYS_COUNT_TX_GREEN_PRIO_3,
},
[OCELOT_STAT_TX_GREEN_PRIO_4] = {
.name = "tx_green_prio_4",
.reg = SYS_COUNT_TX_GREEN_PRIO_4,
},
[OCELOT_STAT_TX_GREEN_PRIO_5] = {
.name = "tx_green_prio_5",
.reg = SYS_COUNT_TX_GREEN_PRIO_5,
},
[OCELOT_STAT_TX_GREEN_PRIO_6] = {
.name = "tx_green_prio_6",
.reg = SYS_COUNT_TX_GREEN_PRIO_6,
},
[OCELOT_STAT_TX_GREEN_PRIO_7] = {
.name = "tx_green_prio_7",
.reg = SYS_COUNT_TX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_AGED] = {
.name = "tx_aged",
.reg = SYS_COUNT_TX_AGING,
},
[OCELOT_STAT_DROP_LOCAL] = {
.name = "drop_local",
.reg = SYS_COUNT_DROP_LOCAL,
},
[OCELOT_STAT_DROP_TAIL] = {
.name = "drop_tail",
.reg = SYS_COUNT_DROP_TAIL,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_0] = {
.name = "drop_yellow_prio_0",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_0,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_1] = {
.name = "drop_yellow_prio_1",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_1,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_2] = {
.name = "drop_yellow_prio_2",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_2,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_3] = {
.name = "drop_yellow_prio_3",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_3,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_4] = {
.name = "drop_yellow_prio_4",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_4,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_5] = {
.name = "drop_yellow_prio_5",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_5,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_6] = {
.name = "drop_yellow_prio_6",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_6,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_7] = {
.name = "drop_yellow_prio_7",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_7,
},
[OCELOT_STAT_DROP_GREEN_PRIO_0] = {
.name = "drop_green_prio_0",
.reg = SYS_COUNT_DROP_GREEN_PRIO_0,
},
[OCELOT_STAT_DROP_GREEN_PRIO_1] = {
.name = "drop_green_prio_1",
.reg = SYS_COUNT_DROP_GREEN_PRIO_1,
},
[OCELOT_STAT_DROP_GREEN_PRIO_2] = {
.name = "drop_green_prio_2",
.reg = SYS_COUNT_DROP_GREEN_PRIO_2,
},
[OCELOT_STAT_DROP_GREEN_PRIO_3] = {
.name = "drop_green_prio_3",
.reg = SYS_COUNT_DROP_GREEN_PRIO_3,
},
[OCELOT_STAT_DROP_GREEN_PRIO_4] = {
.name = "drop_green_prio_4",
.reg = SYS_COUNT_DROP_GREEN_PRIO_4,
},
[OCELOT_STAT_DROP_GREEN_PRIO_5] = {
.name = "drop_green_prio_5",
.reg = SYS_COUNT_DROP_GREEN_PRIO_5,
},
[OCELOT_STAT_DROP_GREEN_PRIO_6] = {
.name = "drop_green_prio_6",
.reg = SYS_COUNT_DROP_GREEN_PRIO_6,
},
[OCELOT_STAT_DROP_GREEN_PRIO_7] = {
.name = "drop_green_prio_7",
.reg = SYS_COUNT_DROP_GREEN_PRIO_7,
},
}; };
static const struct vcap_field vsc9953_vcap_es0_keys[] = { static const struct vcap_field vsc9953_vcap_es0_keys[] = {
......
...@@ -2,16 +2,17 @@ ...@@ -2,16 +2,17 @@
obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o
mscc_ocelot_switch_lib-y := \ mscc_ocelot_switch_lib-y := \
ocelot.o \ ocelot.o \
ocelot_devlink.o \
ocelot_flower.o \
ocelot_io.o \ ocelot_io.o \
ocelot_police.o \ ocelot_police.o \
ocelot_vcap.o \
ocelot_flower.o \
ocelot_ptp.o \ ocelot_ptp.o \
ocelot_devlink.o \ ocelot_stats.o \
ocelot_vcap.o \
vsc7514_regs.o vsc7514_regs.o
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \ mscc_ocelot-y := \
ocelot_fdma.o \ ocelot_fdma.o \
ocelot_vsc7514.o \ ocelot_net.o \
ocelot_net.o ocelot_vsc7514.o
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
*/ */
#include <linux/dsa/ocelot.h> #include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/ptp_classify.h>
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_vcap.h" #include "ocelot_vcap.h"
...@@ -910,211 +909,6 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, ...@@ -910,211 +909,6 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_up); EXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_up);
static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
struct sk_buff *clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
unsigned long flags;
spin_lock_irqsave(&ocelot->ts_id_lock, flags);
if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID ||
ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) {
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
return -EBUSY;
}
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
ocelot_port->ts_id++;
if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID)
ocelot_port->ts_id = 0;
ocelot_port->ptp_skbs_in_flight++;
ocelot->ptp_skbs_in_flight++;
skb_queue_tail(&ocelot_port->tx_skbs, clone);
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
return 0;
}
static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb,
unsigned int ptp_class)
{
struct ptp_header *hdr;
u8 msgtype, twostep;
hdr = ptp_parse_header(skb, ptp_class);
if (!hdr)
return false;
msgtype = ptp_get_msgtype(hdr, ptp_class);
twostep = hdr->flag_field[0] & 0x2;
if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0)
return true;
return false;
}
int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
struct sk_buff *skb,
struct sk_buff **clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u8 ptp_cmd = ocelot_port->ptp_cmd;
unsigned int ptp_class;
int err;
/* Don't do anything if PTP timestamping not enabled */
if (!ptp_cmd)
return 0;
ptp_class = ptp_classify_raw(skb);
if (ptp_class == PTP_CLASS_NONE)
return -EINVAL;
/* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */
if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) {
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
return 0;
}
/* Fall back to two-step timestamping */
ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
}
if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
*clone = skb_clone_sk(skb);
if (!(*clone))
return -ENOMEM;
err = ocelot_port_add_txtstamp_skb(ocelot, port, *clone);
if (err)
return err;
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
OCELOT_SKB_CB(*clone)->ptp_class = ptp_class;
}
return 0;
}
EXPORT_SYMBOL(ocelot_port_txtstamp_request);
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
struct timespec64 *ts)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
/* Read current PTP time to get seconds */
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
/* Read packet HW timestamp from FIFO */
val = ocelot_read(ocelot, SYS_PTP_TXSTAMP);
ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val);
/* Sec has incremented since the ts was registered */
if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC))
ts->tv_sec--;
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
}
static bool ocelot_validate_ptp_skb(struct sk_buff *clone, u16 seqid)
{
struct ptp_header *hdr;
hdr = ptp_parse_header(clone, OCELOT_SKB_CB(clone)->ptp_class);
if (WARN_ON(!hdr))
return false;
return seqid == ntohs(hdr->sequence_id);
}
void ocelot_get_txtstamp(struct ocelot *ocelot)
{
int budget = OCELOT_PTP_QUEUE_SZ;
while (budget--) {
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
struct skb_shared_hwtstamps shhwtstamps;
u32 val, id, seqid, txport;
struct ocelot_port *port;
struct timespec64 ts;
unsigned long flags;
val = ocelot_read(ocelot, SYS_PTP_STATUS);
/* Check if a timestamp can be retrieved */
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
break;
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
/* Retrieve the ts ID and Tx port */
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val);
port = ocelot->ports[txport];
spin_lock(&ocelot->ts_id_lock);
port->ptp_skbs_in_flight--;
ocelot->ptp_skbs_in_flight--;
spin_unlock(&ocelot->ts_id_lock);
/* Retrieve its associated skb */
try_again:
spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
if (OCELOT_SKB_CB(skb)->ts_id != id)
continue;
__skb_unlink(skb, &port->tx_skbs);
skb_match = skb;
break;
}
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
if (WARN_ON(!skb_match))
continue;
if (!ocelot_validate_ptp_skb(skb_match, seqid)) {
dev_err_ratelimited(ocelot->dev,
"port %d received stale TX timestamp for seqid %d, discarding\n",
txport, seqid);
dev_kfree_skb_any(skb);
goto try_again;
}
/* Get the h/w timestamp */
ocelot_get_hwtimestamp(ocelot, &ts);
/* Set the timestamp into the skb */
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
skb_complete_tx_timestamp(skb_match, &shhwtstamps);
/* Next ts */
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
}
}
EXPORT_SYMBOL(ocelot_get_txtstamp);
static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
u32 *rval) u32 *rval)
{ {
...@@ -1366,50 +1160,6 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr, ...@@ -1366,50 +1160,6 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
} }
EXPORT_SYMBOL(ocelot_fdb_del); EXPORT_SYMBOL(ocelot_fdb_del);
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data)
{
struct ocelot_dump_ctx *dump = data;
u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
if (dump->idx < dump->cb->args[2])
goto skip;
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
sizeof(*ndm), NLM_F_MULTI);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
goto nla_put_failure;
nlmsg_end(dump->skb, nlh);
skip:
dump->idx++;
return 0;
nla_put_failure:
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
EXPORT_SYMBOL(ocelot_port_fdb_do_dump);
/* Caller must hold &ocelot->mact_lock */ /* Caller must hold &ocelot->mact_lock */
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry) struct ocelot_mact_entry *entry)
...@@ -1541,53 +1291,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port, ...@@ -1541,53 +1291,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_fdb_dump); EXPORT_SYMBOL(ocelot_fdb_dump);
static void ocelot_populate_l2_ptp_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)trap->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)trap->key.etype.etype.mask = htons(0xffff);
}
static void
ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv4.dport.value = PTP_EV_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_EV_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv4.dport.value = PTP_GEN_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_GEN_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
int ocelot_trap_add(struct ocelot *ocelot, int port, int ocelot_trap_add(struct ocelot *ocelot, int port,
unsigned long cookie, bool take_ts, unsigned long cookie, bool take_ts,
void (*populate)(struct ocelot_vcap_filter *f)) void (*populate)(struct ocelot_vcap_filter *f))
...@@ -1656,381 +1359,6 @@ int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie) ...@@ -1656,381 +1359,6 @@ int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie)
return ocelot_vcap_filter_replace(ocelot, trap); return ocelot_vcap_filter_replace(ocelot, trap);
} }
static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot);
return ocelot_trap_add(ocelot, port, l2_cookie, true,
ocelot_populate_l2_ptp_trap_key);
}
static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot);
return ocelot_trap_del(ocelot, port, l2_cookie);
}
static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot);
unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true,
ocelot_populate_ipv4_ptp_event_trap_key);
if (err)
return err;
err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false,
ocelot_populate_ipv4_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv4_ev_cookie);
return err;
}
static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot);
unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv4_gen_cookie);
return err;
}
static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot);
unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true,
ocelot_populate_ipv6_ptp_event_trap_key);
if (err)
return err;
err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false,
ocelot_populate_ipv6_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv6_ev_cookie);
return err;
}
static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot);
unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv6_gen_cookie);
return err;
}
static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port,
bool l2, bool l4)
{
int err;
if (l2)
err = ocelot_l2_ptp_trap_add(ocelot, port);
else
err = ocelot_l2_ptp_trap_del(ocelot, port);
if (err)
return err;
if (l4) {
err = ocelot_ipv4_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv4;
err = ocelot_ipv6_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv6;
} else {
err = ocelot_ipv4_ptp_trap_del(ocelot, port);
err |= ocelot_ipv6_ptp_trap_del(ocelot, port);
}
if (err)
return err;
return 0;
err_ipv6:
ocelot_ipv4_ptp_trap_del(ocelot, port);
err_ipv4:
if (l2)
ocelot_l2_ptp_trap_del(ocelot, port);
return err;
}
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
}
EXPORT_SYMBOL(ocelot_hwstamp_get);
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
bool l2 = false, l4 = false;
struct hwtstamp_config cfg;
int err;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
/* Tx type sanity check */
switch (cfg.tx_type) {
case HWTSTAMP_TX_ON:
ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
break;
case HWTSTAMP_TX_ONESTEP_SYNC:
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
* need to update the origin time.
*/
ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
break;
case HWTSTAMP_TX_OFF:
ocelot_port->ptp_cmd = 0;
break;
default:
return -ERANGE;
}
mutex_lock(&ocelot->ptp_lock);
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
l4 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
l2 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
l2 = true;
l4 = true;
break;
default:
mutex_unlock(&ocelot->ptp_lock);
return -ERANGE;
}
err = ocelot_setup_ptp_traps(ocelot, port, l2, l4);
if (err) {
mutex_unlock(&ocelot->ptp_lock);
return err;
}
if (l2 && l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else if (l2)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else if (l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else
cfg.rx_filter = HWTSTAMP_FILTER_NONE;
/* Commit back the result & save it */
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
mutex_unlock(&ocelot->ptp_lock);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
}
EXPORT_SYMBOL(ocelot_hwstamp_set);
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
{
int i;
if (sset != ETH_SS_STATS)
return;
for (i = 0; i < OCELOT_NUM_STATS; i++) {
if (ocelot->stats_layout[i].name[0] == '\0')
continue;
memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
ETH_GSTRING_LEN);
}
}
EXPORT_SYMBOL(ocelot_get_strings);
/* Caller must hold &ocelot->stats_lock */
static int ocelot_port_update_stats(struct ocelot *ocelot, int port)
{
unsigned int idx = port * OCELOT_NUM_STATS;
struct ocelot_stats_region *region;
int err, j;
/* Configure the port to read the stats from */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG);
list_for_each_entry(region, &ocelot->stats_regions, node) {
err = ocelot_bulk_read(ocelot, region->base, region->buf,
region->count);
if (err)
return err;
for (j = 0; j < region->count; j++) {
u64 *stat = &ocelot->stats[idx + j];
u64 val = region->buf[j];
if (val < (*stat & U32_MAX))
*stat += (u64)1 << 32;
*stat = (*stat & ~(u64)U32_MAX) + val;
}
idx += region->count;
}
return err;
}
static void ocelot_check_stats_work(struct work_struct *work)
{
struct delayed_work *del_work = to_delayed_work(work);
struct ocelot *ocelot = container_of(del_work, struct ocelot,
stats_work);
int i, err;
spin_lock(&ocelot->stats_lock);
for (i = 0; i < ocelot->num_phys_ports; i++) {
err = ocelot_port_update_stats(ocelot, i);
if (err)
break;
}
spin_unlock(&ocelot->stats_lock);
if (err)
dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
OCELOT_STATS_CHECK_DELAY);
}
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
{
int i, err;
spin_lock(&ocelot->stats_lock);
/* check and update now */
err = ocelot_port_update_stats(ocelot, port);
/* Copy all supported counters */
for (i = 0; i < OCELOT_NUM_STATS; i++) {
int index = port * OCELOT_NUM_STATS + i;
if (ocelot->stats_layout[i].name[0] == '\0')
continue;
*data++ = ocelot->stats[index];
}
spin_unlock(&ocelot->stats_lock);
if (err)
dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
}
EXPORT_SYMBOL(ocelot_get_ethtool_stats);
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{
int i, num_stats = 0;
if (sset != ETH_SS_STATS)
return -EOPNOTSUPP;
for (i = 0; i < OCELOT_NUM_STATS; i++)
if (ocelot->stats_layout[i].name[0] != '\0')
num_stats++;
return num_stats;
}
EXPORT_SYMBOL(ocelot_get_sset_count);
static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
{
struct ocelot_stats_region *region = NULL;
unsigned int last;
int i;
INIT_LIST_HEAD(&ocelot->stats_regions);
for (i = 0; i < OCELOT_NUM_STATS; i++) {
if (ocelot->stats_layout[i].name[0] == '\0')
continue;
if (region && ocelot->stats_layout[i].reg == last + 4) {
region->count++;
} else {
region = devm_kzalloc(ocelot->dev, sizeof(*region),
GFP_KERNEL);
if (!region)
return -ENOMEM;
region->base = ocelot->stats_layout[i].reg;
region->count = 1;
list_add_tail(&region->node, &ocelot->stats_regions);
}
last = ocelot->stats_layout[i].reg;
}
list_for_each_entry(region, &ocelot->stats_regions, node) {
region->buf = devm_kcalloc(ocelot->dev, region->count,
sizeof(*region->buf), GFP_KERNEL);
if (!region->buf)
return -ENOMEM;
}
return 0;
}
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info)
{
info->phc_index = ocelot->ptp_clock ?
ptp_clock_index(ocelot->ptp_clock) : -1;
if (info->phc_index == -1) {
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
return 0;
}
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
return 0;
}
EXPORT_SYMBOL(ocelot_get_ts_info);
static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond) static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond)
{ {
u32 mask = 0; u32 mask = 0;
...@@ -3377,7 +2705,6 @@ static void ocelot_detect_features(struct ocelot *ocelot) ...@@ -3377,7 +2705,6 @@ static void ocelot_detect_features(struct ocelot *ocelot)
int ocelot_init(struct ocelot *ocelot) int ocelot_init(struct ocelot *ocelot)
{ {
char queue_name[32];
int i, ret; int i, ret;
u32 port; u32 port;
...@@ -3389,29 +2716,21 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -3389,29 +2716,21 @@ int ocelot_init(struct ocelot *ocelot)
} }
} }
ocelot->stats = devm_kcalloc(ocelot->dev,
ocelot->num_phys_ports * OCELOT_NUM_STATS,
sizeof(u64), GFP_KERNEL);
if (!ocelot->stats)
return -ENOMEM;
spin_lock_init(&ocelot->stats_lock);
mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->ptp_lock);
mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->mact_lock);
mutex_init(&ocelot->fwd_domain_lock); mutex_init(&ocelot->fwd_domain_lock);
mutex_init(&ocelot->tas_lock); mutex_init(&ocelot->tas_lock);
spin_lock_init(&ocelot->ptp_clock_lock); spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock); spin_lock_init(&ocelot->ts_id_lock);
snprintf(queue_name, sizeof(queue_name), "%s-stats",
dev_name(ocelot->dev));
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
if (!ocelot->stats_queue)
return -ENOMEM;
ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0); ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0);
if (!ocelot->owq) { if (!ocelot->owq)
destroy_workqueue(ocelot->stats_queue);
return -ENOMEM; return -ENOMEM;
ret = ocelot_stats_init(ocelot);
if (ret) {
destroy_workqueue(ocelot->owq);
return ret;
} }
INIT_LIST_HEAD(&ocelot->multicast); INIT_LIST_HEAD(&ocelot->multicast);
...@@ -3523,25 +2842,13 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -3523,25 +2842,13 @@ int ocelot_init(struct ocelot *ocelot)
ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6), ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6),
ANA_CPUQ_8021_CFG, i); ANA_CPUQ_8021_CFG, i);
ret = ocelot_prepare_stats_regions(ocelot);
if (ret) {
destroy_workqueue(ocelot->stats_queue);
destroy_workqueue(ocelot->owq);
return ret;
}
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
OCELOT_STATS_CHECK_DELAY);
return 0; return 0;
} }
EXPORT_SYMBOL(ocelot_init); EXPORT_SYMBOL(ocelot_init);
void ocelot_deinit(struct ocelot *ocelot) void ocelot_deinit(struct ocelot *ocelot)
{ {
cancel_delayed_work(&ocelot->stats_work); ocelot_stats_deinit(ocelot);
destroy_workqueue(ocelot->stats_queue);
destroy_workqueue(ocelot->owq); destroy_workqueue(ocelot->owq);
} }
EXPORT_SYMBOL(ocelot_deinit); EXPORT_SYMBOL(ocelot_deinit);
......
...@@ -51,13 +51,6 @@ struct ocelot_port_private { ...@@ -51,13 +51,6 @@ struct ocelot_port_private {
struct ocelot_port_tc tc; struct ocelot_port_tc tc;
}; };
struct ocelot_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
/* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports /* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports
* possibilities of egress port masks for L2 multicast traffic. * possibilities of egress port masks for L2 multicast traffic.
* For a switch with 9 user ports, there are 512 possible port masks, but the * For a switch with 9 user ports, there are 512 possible port masks, but the
...@@ -84,8 +77,6 @@ struct ocelot_multicast { ...@@ -84,8 +77,6 @@ struct ocelot_multicast {
int ocelot_bridge_num_find(struct ocelot *ocelot, int ocelot_bridge_num_find(struct ocelot *ocelot,
const struct net_device *bridge); const struct net_device *bridge);
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data);
int ocelot_mact_learn(struct ocelot *ocelot, int port, int ocelot_mact_learn(struct ocelot *ocelot, int port,
const unsigned char mac[ETH_ALEN], const unsigned char mac[ETH_ALEN],
unsigned int vid, enum macaccess_entry_type type); unsigned int vid, enum macaccess_entry_type type);
...@@ -115,6 +106,9 @@ struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to, ...@@ -115,6 +106,9 @@ struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
void ocelot_mirror_put(struct ocelot *ocelot); void ocelot_mirror_put(struct ocelot *ocelot);
int ocelot_stats_init(struct ocelot *ocelot);
void ocelot_stats_deinit(struct ocelot *ocelot);
extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb; extern struct notifier_block ocelot_switchdev_blocking_nb;
......
...@@ -20,6 +20,13 @@ ...@@ -20,6 +20,13 @@
#define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP
struct ocelot_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
static bool ocelot_netdevice_dev_check(const struct net_device *dev); static bool ocelot_netdevice_dev_check(const struct net_device *dev);
static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp)
...@@ -725,42 +732,8 @@ static void ocelot_get_stats64(struct net_device *dev, ...@@ -725,42 +732,8 @@ static void ocelot_get_stats64(struct net_device *dev,
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->port.index; int port = priv->port.index;
u64 *s;
return ocelot_port_get_stats64(ocelot, port, stats);
spin_lock(&ocelot->stats_lock);
s = &ocelot->stats[port * OCELOT_NUM_STATS];
/* Get Rx stats */
stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS];
stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] +
s[OCELOT_STAT_RX_FRAGMENTS] +
s[OCELOT_STAT_RX_JABBERS] +
s[OCELOT_STAT_RX_LONGS] +
s[OCELOT_STAT_RX_64] +
s[OCELOT_STAT_RX_65_127] +
s[OCELOT_STAT_RX_128_255] +
s[OCELOT_STAT_RX_256_511] +
s[OCELOT_STAT_RX_512_1023] +
s[OCELOT_STAT_RX_1024_1526] +
s[OCELOT_STAT_RX_1527_MAX];
stats->multicast = s[OCELOT_STAT_RX_MULTICAST];
stats->rx_dropped = dev->stats.rx_dropped;
/* Get Tx stats */
stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS];
stats->tx_packets = s[OCELOT_STAT_TX_64] +
s[OCELOT_STAT_TX_65_127] +
s[OCELOT_STAT_TX_128_255] +
s[OCELOT_STAT_TX_256_511] +
s[OCELOT_STAT_TX_512_1023] +
s[OCELOT_STAT_TX_1024_1526] +
s[OCELOT_STAT_TX_1527_MAX];
stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] +
s[OCELOT_STAT_TX_AGED];
stats->collisions = s[OCELOT_STAT_TX_COLLISION];
spin_unlock(&ocelot->stats_lock);
} }
static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
...@@ -790,6 +763,49 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -790,6 +763,49 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge); return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge);
} }
static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data)
{
struct ocelot_dump_ctx *dump = data;
u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
if (dump->idx < dump->cb->args[2])
goto skip;
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
sizeof(*ndm), NLM_F_MULTI);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
goto nla_put_failure;
nlmsg_end(dump->skb, nlh);
skip:
dump->idx++;
return 0;
nla_put_failure:
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
static int ocelot_port_fdb_dump(struct sk_buff *skb, static int ocelot_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *dev,
......
...@@ -6,9 +6,13 @@ ...@@ -6,9 +6,13 @@
*/ */
#include <linux/time64.h> #include <linux/time64.h>
#include <linux/dsa/ocelot.h>
#include <linux/ptp_classify.h>
#include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include "ocelot.h"
int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
{ {
...@@ -310,6 +314,483 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp, ...@@ -310,6 +314,483 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp,
} }
EXPORT_SYMBOL(ocelot_ptp_enable); EXPORT_SYMBOL(ocelot_ptp_enable);
static void ocelot_populate_l2_ptp_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)trap->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)trap->key.etype.etype.mask = htons(0xffff);
}
static void
ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv4.dport.value = PTP_EV_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_EV_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV4;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv4.dport.value = PTP_GEN_PORT;
trap->key.ipv4.dport.mask = 0xffff;
}
static void
ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
trap->key.ipv4.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_GEN_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot);
return ocelot_trap_add(ocelot, port, l2_cookie, true,
ocelot_populate_l2_ptp_trap_key);
}
static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot);
return ocelot_trap_del(ocelot, port, l2_cookie);
}
static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot);
unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true,
ocelot_populate_ipv4_ptp_event_trap_key);
if (err)
return err;
err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false,
ocelot_populate_ipv4_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv4_ev_cookie);
return err;
}
static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot);
unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv4_gen_cookie);
return err;
}
static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot);
unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true,
ocelot_populate_ipv6_ptp_event_trap_key);
if (err)
return err;
err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false,
ocelot_populate_ipv6_ptp_general_trap_key);
if (err)
ocelot_trap_del(ocelot, port, ipv6_ev_cookie);
return err;
}
static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port)
{
unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot);
unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot);
int err;
err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie);
err |= ocelot_trap_del(ocelot, port, ipv6_gen_cookie);
return err;
}
static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port,
bool l2, bool l4)
{
int err;
if (l2)
err = ocelot_l2_ptp_trap_add(ocelot, port);
else
err = ocelot_l2_ptp_trap_del(ocelot, port);
if (err)
return err;
if (l4) {
err = ocelot_ipv4_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv4;
err = ocelot_ipv6_ptp_trap_add(ocelot, port);
if (err)
goto err_ipv6;
} else {
err = ocelot_ipv4_ptp_trap_del(ocelot, port);
err |= ocelot_ipv6_ptp_trap_del(ocelot, port);
}
if (err)
return err;
return 0;
err_ipv6:
ocelot_ipv4_ptp_trap_del(ocelot, port);
err_ipv4:
if (l2)
ocelot_l2_ptp_trap_del(ocelot, port);
return err;
}
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
}
EXPORT_SYMBOL(ocelot_hwstamp_get);
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
bool l2 = false, l4 = false;
struct hwtstamp_config cfg;
int err;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
/* Tx type sanity check */
switch (cfg.tx_type) {
case HWTSTAMP_TX_ON:
ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
break;
case HWTSTAMP_TX_ONESTEP_SYNC:
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
* need to update the origin time.
*/
ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
break;
case HWTSTAMP_TX_OFF:
ocelot_port->ptp_cmd = 0;
break;
default:
return -ERANGE;
}
mutex_lock(&ocelot->ptp_lock);
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
l4 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
l2 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
l2 = true;
l4 = true;
break;
default:
mutex_unlock(&ocelot->ptp_lock);
return -ERANGE;
}
err = ocelot_setup_ptp_traps(ocelot, port, l2, l4);
if (err) {
mutex_unlock(&ocelot->ptp_lock);
return err;
}
if (l2 && l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else if (l2)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else if (l4)
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else
cfg.rx_filter = HWTSTAMP_FILTER_NONE;
/* Commit back the result & save it */
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
mutex_unlock(&ocelot->ptp_lock);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
}
EXPORT_SYMBOL(ocelot_hwstamp_set);
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info)
{
info->phc_index = ocelot->ptp_clock ?
ptp_clock_index(ocelot->ptp_clock) : -1;
if (info->phc_index == -1) {
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
return 0;
}
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
return 0;
}
EXPORT_SYMBOL(ocelot_get_ts_info);
static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
struct sk_buff *clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
unsigned long flags;
spin_lock_irqsave(&ocelot->ts_id_lock, flags);
if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID ||
ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) {
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
return -EBUSY;
}
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
/* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
ocelot_port->ts_id++;
if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID)
ocelot_port->ts_id = 0;
ocelot_port->ptp_skbs_in_flight++;
ocelot->ptp_skbs_in_flight++;
skb_queue_tail(&ocelot_port->tx_skbs, clone);
spin_unlock_irqrestore(&ocelot->ts_id_lock, flags);
return 0;
}
static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb,
unsigned int ptp_class)
{
struct ptp_header *hdr;
u8 msgtype, twostep;
hdr = ptp_parse_header(skb, ptp_class);
if (!hdr)
return false;
msgtype = ptp_get_msgtype(hdr, ptp_class);
twostep = hdr->flag_field[0] & 0x2;
if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0)
return true;
return false;
}
int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
struct sk_buff *skb,
struct sk_buff **clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u8 ptp_cmd = ocelot_port->ptp_cmd;
unsigned int ptp_class;
int err;
/* Don't do anything if PTP timestamping not enabled */
if (!ptp_cmd)
return 0;
ptp_class = ptp_classify_raw(skb);
if (ptp_class == PTP_CLASS_NONE)
return -EINVAL;
/* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */
if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) {
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
return 0;
}
/* Fall back to two-step timestamping */
ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
}
if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
*clone = skb_clone_sk(skb);
if (!(*clone))
return -ENOMEM;
err = ocelot_port_add_txtstamp_skb(ocelot, port, *clone);
if (err)
return err;
OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
OCELOT_SKB_CB(*clone)->ptp_class = ptp_class;
}
return 0;
}
EXPORT_SYMBOL(ocelot_port_txtstamp_request);
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
struct timespec64 *ts)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
/* Read current PTP time to get seconds */
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
/* Read packet HW timestamp from FIFO */
val = ocelot_read(ocelot, SYS_PTP_TXSTAMP);
ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val);
/* Sec has incremented since the ts was registered */
if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC))
ts->tv_sec--;
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
}
static bool ocelot_validate_ptp_skb(struct sk_buff *clone, u16 seqid)
{
struct ptp_header *hdr;
hdr = ptp_parse_header(clone, OCELOT_SKB_CB(clone)->ptp_class);
if (WARN_ON(!hdr))
return false;
return seqid == ntohs(hdr->sequence_id);
}
void ocelot_get_txtstamp(struct ocelot *ocelot)
{
int budget = OCELOT_PTP_QUEUE_SZ;
while (budget--) {
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
struct skb_shared_hwtstamps shhwtstamps;
u32 val, id, seqid, txport;
struct ocelot_port *port;
struct timespec64 ts;
unsigned long flags;
val = ocelot_read(ocelot, SYS_PTP_STATUS);
/* Check if a timestamp can be retrieved */
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
break;
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
/* Retrieve the ts ID and Tx port */
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val);
port = ocelot->ports[txport];
spin_lock(&ocelot->ts_id_lock);
port->ptp_skbs_in_flight--;
ocelot->ptp_skbs_in_flight--;
spin_unlock(&ocelot->ts_id_lock);
/* Retrieve its associated skb */
try_again:
spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
if (OCELOT_SKB_CB(skb)->ts_id != id)
continue;
__skb_unlink(skb, &port->tx_skbs);
skb_match = skb;
break;
}
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
if (WARN_ON(!skb_match))
continue;
if (!ocelot_validate_ptp_skb(skb_match, seqid)) {
dev_err_ratelimited(ocelot->dev,
"port %d received stale TX timestamp for seqid %d, discarding\n",
txport, seqid);
dev_kfree_skb_any(skb);
goto try_again;
}
/* Get the h/w timestamp */
ocelot_get_hwtimestamp(ocelot, &ts);
/* Set the timestamp into the skb */
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
skb_complete_tx_timestamp(skb_match, &shhwtstamps);
/* Next ts */
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
}
}
EXPORT_SYMBOL(ocelot_get_txtstamp);
int ocelot_init_timestamp(struct ocelot *ocelot, int ocelot_init_timestamp(struct ocelot *ocelot,
const struct ptp_clock_info *info) const struct ptp_clock_info *info)
{ {
......
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Statistics for Ocelot switch family
*
* Copyright (c) 2017 Microsemi Corporation
* Copyright 2022 NXP
*/
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "ocelot.h"
/* Read the counters from hardware and keep them in region->buf.
* Caller must hold &ocelot->stat_view_lock.
*/
static int ocelot_port_update_stats(struct ocelot *ocelot, int port)
{
struct ocelot_stats_region *region;
int err;
/* Configure the port to read the stats from */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG);
list_for_each_entry(region, &ocelot->stats_regions, node) {
err = ocelot_bulk_read(ocelot, region->base, region->buf,
region->count);
if (err)
return err;
}
return 0;
}
/* Transfer the counters from region->buf to ocelot->stats.
* Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock.
*/
static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port)
{
unsigned int idx = port * OCELOT_NUM_STATS;
struct ocelot_stats_region *region;
int j;
list_for_each_entry(region, &ocelot->stats_regions, node) {
for (j = 0; j < region->count; j++) {
u64 *stat = &ocelot->stats[idx + j];
u64 val = region->buf[j];
if (val < (*stat & U32_MAX))
*stat += (u64)1 << 32;
*stat = (*stat & ~(u64)U32_MAX) + val;
}
idx += region->count;
}
}
static void ocelot_check_stats_work(struct work_struct *work)
{
struct delayed_work *del_work = to_delayed_work(work);
struct ocelot *ocelot = container_of(del_work, struct ocelot,
stats_work);
int port, err;
mutex_lock(&ocelot->stat_view_lock);
for (port = 0; port < ocelot->num_phys_ports; port++) {
err = ocelot_port_update_stats(ocelot, port);
if (err)
break;
spin_lock(&ocelot->stats_lock);
ocelot_port_transfer_stats(ocelot, port);
spin_unlock(&ocelot->stats_lock);
}
if (!err && ocelot->ops->update_stats)
ocelot->ops->update_stats(ocelot);
mutex_unlock(&ocelot->stat_view_lock);
if (err)
dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
OCELOT_STATS_CHECK_DELAY);
}
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
{
int i;
if (sset != ETH_SS_STATS)
return;
for (i = 0; i < OCELOT_NUM_STATS; i++) {
if (ocelot->stats_layout[i].name[0] == '\0')
continue;
memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
ETH_GSTRING_LEN);
}
}
EXPORT_SYMBOL(ocelot_get_strings);
/* Update ocelot->stats for the given port and run the given callback */
static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv,
void (*cb)(struct ocelot *ocelot, int port,
void *priv))
{
int err;
mutex_lock(&ocelot->stat_view_lock);
err = ocelot_port_update_stats(ocelot, port);
if (err) {
dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n",
port, ERR_PTR(err));
goto out_unlock;
}
spin_lock(&ocelot->stats_lock);
ocelot_port_transfer_stats(ocelot, port);
cb(ocelot, port, priv);
spin_unlock(&ocelot->stats_lock);
out_unlock:
mutex_unlock(&ocelot->stat_view_lock);
}
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{
int i, num_stats = 0;
if (sset != ETH_SS_STATS)
return -EOPNOTSUPP;
for (i = 0; i < OCELOT_NUM_STATS; i++)
if (ocelot->stats_layout[i].name[0] != '\0')
num_stats++;
return num_stats;
}
EXPORT_SYMBOL(ocelot_get_sset_count);
static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
void *priv)
{
u64 *data = priv;
int i;
/* Copy all supported counters */
for (i = 0; i < OCELOT_NUM_STATS; i++) {
int index = port * OCELOT_NUM_STATS + i;
if (ocelot->stats_layout[i].name[0] == '\0')
continue;
*data++ = ocelot->stats[index];
}
}
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
{
ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb);
}
EXPORT_SYMBOL(ocelot_get_ethtool_stats);
static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
struct ethtool_pause_stats *pause_stats = priv;
pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE];
pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE];
}
void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
struct ethtool_pause_stats *pause_stats)
{
ocelot_port_stats_run(ocelot, port, pause_stats,
ocelot_port_pause_stats_cb);
}
EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = {
{ 64, 64 },
{ 65, 127 },
{ 128, 255 },
{ 256, 511 },
{ 512, 1023 },
{ 1024, 1526 },
{ 1527, 65535 },
{},
};
static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
struct ethtool_rmon_stats *rmon_stats = priv;
rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS];
rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS];
rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS];
rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS];
rmon_stats->hist[0] = s[OCELOT_STAT_RX_64];
rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127];
rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255];
rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511];
rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023];
rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526];
rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX];
rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64];
rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127];
rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255];
rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255];
rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511];
rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023];
rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526];
}
void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
*ranges = ocelot_rmon_ranges;
ocelot_port_stats_run(ocelot, port, rmon_stats,
ocelot_port_rmon_stats_cb);
}
EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats);
static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL];
}
void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
ocelot_port_stats_run(ocelot, port, ctrl_stats,
ocelot_port_ctrl_stats_cb);
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats);
static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
struct ethtool_eth_mac_stats *mac_stats = priv;
mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS];
mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] +
s[OCELOT_STAT_TX_65_127] +
s[OCELOT_STAT_TX_128_255] +
s[OCELOT_STAT_TX_256_511] +
s[OCELOT_STAT_TX_512_1023] +
s[OCELOT_STAT_TX_1024_1526] +
s[OCELOT_STAT_TX_1527_MAX];
mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS];
mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] +
s[OCELOT_STAT_RX_GREEN_PRIO_1] +
s[OCELOT_STAT_RX_GREEN_PRIO_2] +
s[OCELOT_STAT_RX_GREEN_PRIO_3] +
s[OCELOT_STAT_RX_GREEN_PRIO_4] +
s[OCELOT_STAT_RX_GREEN_PRIO_5] +
s[OCELOT_STAT_RX_GREEN_PRIO_6] +
s[OCELOT_STAT_RX_GREEN_PRIO_7] +
s[OCELOT_STAT_RX_YELLOW_PRIO_0] +
s[OCELOT_STAT_RX_YELLOW_PRIO_1] +
s[OCELOT_STAT_RX_YELLOW_PRIO_2] +
s[OCELOT_STAT_RX_YELLOW_PRIO_3] +
s[OCELOT_STAT_RX_YELLOW_PRIO_4] +
s[OCELOT_STAT_RX_YELLOW_PRIO_5] +
s[OCELOT_STAT_RX_YELLOW_PRIO_6] +
s[OCELOT_STAT_RX_YELLOW_PRIO_7];
mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST];
mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST];
mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST];
mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST];
mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS];
/* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
* counted individually.
*/
mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
}
void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_mac_stats *mac_stats)
{
ocelot_port_stats_run(ocelot, port, mac_stats,
ocelot_port_mac_stats_cb);
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats);
static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
struct ethtool_eth_phy_stats *phy_stats = priv;
phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS];
}
void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_phy_stats *phy_stats)
{
ocelot_port_stats_run(ocelot, port, phy_stats,
ocelot_port_phy_stats_cb);
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
struct rtnl_link_stats64 *stats)
{
u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
spin_lock(&ocelot->stats_lock);
/* Get Rx stats */
stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS];
stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] +
s[OCELOT_STAT_RX_FRAGMENTS] +
s[OCELOT_STAT_RX_JABBERS] +
s[OCELOT_STAT_RX_LONGS] +
s[OCELOT_STAT_RX_64] +
s[OCELOT_STAT_RX_65_127] +
s[OCELOT_STAT_RX_128_255] +
s[OCELOT_STAT_RX_256_511] +
s[OCELOT_STAT_RX_512_1023] +
s[OCELOT_STAT_RX_1024_1526] +
s[OCELOT_STAT_RX_1527_MAX];
stats->multicast = s[OCELOT_STAT_RX_MULTICAST];
stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL];
stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] +
s[OCELOT_STAT_RX_RED_PRIO_1] +
s[OCELOT_STAT_RX_RED_PRIO_2] +
s[OCELOT_STAT_RX_RED_PRIO_3] +
s[OCELOT_STAT_RX_RED_PRIO_4] +
s[OCELOT_STAT_RX_RED_PRIO_5] +
s[OCELOT_STAT_RX_RED_PRIO_6] +
s[OCELOT_STAT_RX_RED_PRIO_7] +
s[OCELOT_STAT_DROP_LOCAL] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_0] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_1] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_2] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_3] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_4] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_5] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_6] +
s[OCELOT_STAT_DROP_YELLOW_PRIO_7] +
s[OCELOT_STAT_DROP_GREEN_PRIO_0] +
s[OCELOT_STAT_DROP_GREEN_PRIO_1] +
s[OCELOT_STAT_DROP_GREEN_PRIO_2] +
s[OCELOT_STAT_DROP_GREEN_PRIO_3] +
s[OCELOT_STAT_DROP_GREEN_PRIO_4] +
s[OCELOT_STAT_DROP_GREEN_PRIO_5] +
s[OCELOT_STAT_DROP_GREEN_PRIO_6] +
s[OCELOT_STAT_DROP_GREEN_PRIO_7];
/* Get Tx stats */
stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS];
stats->tx_packets = s[OCELOT_STAT_TX_64] +
s[OCELOT_STAT_TX_65_127] +
s[OCELOT_STAT_TX_128_255] +
s[OCELOT_STAT_TX_256_511] +
s[OCELOT_STAT_TX_512_1023] +
s[OCELOT_STAT_TX_1024_1526] +
s[OCELOT_STAT_TX_1527_MAX];
stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] +
s[OCELOT_STAT_TX_AGED];
stats->collisions = s[OCELOT_STAT_TX_COLLISION];
spin_unlock(&ocelot->stats_lock);
}
EXPORT_SYMBOL(ocelot_port_get_stats64);
static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
{
struct ocelot_stats_region *region = NULL;
unsigned int last;
int i;
INIT_LIST_HEAD(&ocelot->stats_regions);
for (i = 0; i < OCELOT_NUM_STATS; i++) {
if (!ocelot->stats_layout[i].reg)
continue;
if (region && ocelot->stats_layout[i].reg == last + 4) {
region->count++;
} else {
region = devm_kzalloc(ocelot->dev, sizeof(*region),
GFP_KERNEL);
if (!region)
return -ENOMEM;
region->base = ocelot->stats_layout[i].reg;
region->count = 1;
list_add_tail(&region->node, &ocelot->stats_regions);
}
last = ocelot->stats_layout[i].reg;
}
list_for_each_entry(region, &ocelot->stats_regions, node) {
region->buf = devm_kcalloc(ocelot->dev, region->count,
sizeof(*region->buf), GFP_KERNEL);
if (!region->buf)
return -ENOMEM;
}
return 0;
}
int ocelot_stats_init(struct ocelot *ocelot)
{
char queue_name[32];
int ret;
ocelot->stats = devm_kcalloc(ocelot->dev,
ocelot->num_phys_ports * OCELOT_NUM_STATS,
sizeof(u64), GFP_KERNEL);
if (!ocelot->stats)
return -ENOMEM;
snprintf(queue_name, sizeof(queue_name), "%s-stats",
dev_name(ocelot->dev));
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
if (!ocelot->stats_queue)
return -ENOMEM;
spin_lock_init(&ocelot->stats_lock);
mutex_init(&ocelot->stat_view_lock);
ret = ocelot_prepare_stats_regions(ocelot);
if (ret) {
destroy_workqueue(ocelot->stats_queue);
return ret;
}
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
OCELOT_STATS_CHECK_DELAY);
return 0;
}
void ocelot_stats_deinit(struct ocelot *ocelot)
{
cancel_delayed_work(&ocelot->stats_work);
destroy_workqueue(ocelot->stats_queue);
}
...@@ -97,378 +97,7 @@ static const struct reg_field ocelot_regfields[REGFIELD_MAX] = { ...@@ -97,378 +97,7 @@ static const struct reg_field ocelot_regfields[REGFIELD_MAX] = {
}; };
static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = {
[OCELOT_STAT_RX_OCTETS] = { OCELOT_COMMON_STATS,
.name = "rx_octets",
.reg = SYS_COUNT_RX_OCTETS,
},
[OCELOT_STAT_RX_UNICAST] = {
.name = "rx_unicast",
.reg = SYS_COUNT_RX_UNICAST,
},
[OCELOT_STAT_RX_MULTICAST] = {
.name = "rx_multicast",
.reg = SYS_COUNT_RX_MULTICAST,
},
[OCELOT_STAT_RX_BROADCAST] = {
.name = "rx_broadcast",
.reg = SYS_COUNT_RX_BROADCAST,
},
[OCELOT_STAT_RX_SHORTS] = {
.name = "rx_shorts",
.reg = SYS_COUNT_RX_SHORTS,
},
[OCELOT_STAT_RX_FRAGMENTS] = {
.name = "rx_fragments",
.reg = SYS_COUNT_RX_FRAGMENTS,
},
[OCELOT_STAT_RX_JABBERS] = {
.name = "rx_jabbers",
.reg = SYS_COUNT_RX_JABBERS,
},
[OCELOT_STAT_RX_CRC_ALIGN_ERRS] = {
.name = "rx_crc_align_errs",
.reg = SYS_COUNT_RX_CRC_ALIGN_ERRS,
},
[OCELOT_STAT_RX_SYM_ERRS] = {
.name = "rx_sym_errs",
.reg = SYS_COUNT_RX_SYM_ERRS,
},
[OCELOT_STAT_RX_64] = {
.name = "rx_frames_below_65_octets",
.reg = SYS_COUNT_RX_64,
},
[OCELOT_STAT_RX_65_127] = {
.name = "rx_frames_65_to_127_octets",
.reg = SYS_COUNT_RX_65_127,
},
[OCELOT_STAT_RX_128_255] = {
.name = "rx_frames_128_to_255_octets",
.reg = SYS_COUNT_RX_128_255,
},
[OCELOT_STAT_RX_256_511] = {
.name = "rx_frames_256_to_511_octets",
.reg = SYS_COUNT_RX_256_511,
},
[OCELOT_STAT_RX_512_1023] = {
.name = "rx_frames_512_to_1023_octets",
.reg = SYS_COUNT_RX_512_1023,
},
[OCELOT_STAT_RX_1024_1526] = {
.name = "rx_frames_1024_to_1526_octets",
.reg = SYS_COUNT_RX_1024_1526,
},
[OCELOT_STAT_RX_1527_MAX] = {
.name = "rx_frames_over_1526_octets",
.reg = SYS_COUNT_RX_1527_MAX,
},
[OCELOT_STAT_RX_PAUSE] = {
.name = "rx_pause",
.reg = SYS_COUNT_RX_PAUSE,
},
[OCELOT_STAT_RX_CONTROL] = {
.name = "rx_control",
.reg = SYS_COUNT_RX_CONTROL,
},
[OCELOT_STAT_RX_LONGS] = {
.name = "rx_longs",
.reg = SYS_COUNT_RX_LONGS,
},
[OCELOT_STAT_RX_CLASSIFIED_DROPS] = {
.name = "rx_classified_drops",
.reg = SYS_COUNT_RX_CLASSIFIED_DROPS,
},
[OCELOT_STAT_RX_RED_PRIO_0] = {
.name = "rx_red_prio_0",
.reg = SYS_COUNT_RX_RED_PRIO_0,
},
[OCELOT_STAT_RX_RED_PRIO_1] = {
.name = "rx_red_prio_1",
.reg = SYS_COUNT_RX_RED_PRIO_1,
},
[OCELOT_STAT_RX_RED_PRIO_2] = {
.name = "rx_red_prio_2",
.reg = SYS_COUNT_RX_RED_PRIO_2,
},
[OCELOT_STAT_RX_RED_PRIO_3] = {
.name = "rx_red_prio_3",
.reg = SYS_COUNT_RX_RED_PRIO_3,
},
[OCELOT_STAT_RX_RED_PRIO_4] = {
.name = "rx_red_prio_4",
.reg = SYS_COUNT_RX_RED_PRIO_4,
},
[OCELOT_STAT_RX_RED_PRIO_5] = {
.name = "rx_red_prio_5",
.reg = SYS_COUNT_RX_RED_PRIO_5,
},
[OCELOT_STAT_RX_RED_PRIO_6] = {
.name = "rx_red_prio_6",
.reg = SYS_COUNT_RX_RED_PRIO_6,
},
[OCELOT_STAT_RX_RED_PRIO_7] = {
.name = "rx_red_prio_7",
.reg = SYS_COUNT_RX_RED_PRIO_7,
},
[OCELOT_STAT_RX_YELLOW_PRIO_0] = {
.name = "rx_yellow_prio_0",
.reg = SYS_COUNT_RX_YELLOW_PRIO_0,
},
[OCELOT_STAT_RX_YELLOW_PRIO_1] = {
.name = "rx_yellow_prio_1",
.reg = SYS_COUNT_RX_YELLOW_PRIO_1,
},
[OCELOT_STAT_RX_YELLOW_PRIO_2] = {
.name = "rx_yellow_prio_2",
.reg = SYS_COUNT_RX_YELLOW_PRIO_2,
},
[OCELOT_STAT_RX_YELLOW_PRIO_3] = {
.name = "rx_yellow_prio_3",
.reg = SYS_COUNT_RX_YELLOW_PRIO_3,
},
[OCELOT_STAT_RX_YELLOW_PRIO_4] = {
.name = "rx_yellow_prio_4",
.reg = SYS_COUNT_RX_YELLOW_PRIO_4,
},
[OCELOT_STAT_RX_YELLOW_PRIO_5] = {
.name = "rx_yellow_prio_5",
.reg = SYS_COUNT_RX_YELLOW_PRIO_5,
},
[OCELOT_STAT_RX_YELLOW_PRIO_6] = {
.name = "rx_yellow_prio_6",
.reg = SYS_COUNT_RX_YELLOW_PRIO_6,
},
[OCELOT_STAT_RX_YELLOW_PRIO_7] = {
.name = "rx_yellow_prio_7",
.reg = SYS_COUNT_RX_YELLOW_PRIO_7,
},
[OCELOT_STAT_RX_GREEN_PRIO_0] = {
.name = "rx_green_prio_0",
.reg = SYS_COUNT_RX_GREEN_PRIO_0,
},
[OCELOT_STAT_RX_GREEN_PRIO_1] = {
.name = "rx_green_prio_1",
.reg = SYS_COUNT_RX_GREEN_PRIO_1,
},
[OCELOT_STAT_RX_GREEN_PRIO_2] = {
.name = "rx_green_prio_2",
.reg = SYS_COUNT_RX_GREEN_PRIO_2,
},
[OCELOT_STAT_RX_GREEN_PRIO_3] = {
.name = "rx_green_prio_3",
.reg = SYS_COUNT_RX_GREEN_PRIO_3,
},
[OCELOT_STAT_RX_GREEN_PRIO_4] = {
.name = "rx_green_prio_4",
.reg = SYS_COUNT_RX_GREEN_PRIO_4,
},
[OCELOT_STAT_RX_GREEN_PRIO_5] = {
.name = "rx_green_prio_5",
.reg = SYS_COUNT_RX_GREEN_PRIO_5,
},
[OCELOT_STAT_RX_GREEN_PRIO_6] = {
.name = "rx_green_prio_6",
.reg = SYS_COUNT_RX_GREEN_PRIO_6,
},
[OCELOT_STAT_RX_GREEN_PRIO_7] = {
.name = "rx_green_prio_7",
.reg = SYS_COUNT_RX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_OCTETS] = {
.name = "tx_octets",
.reg = SYS_COUNT_TX_OCTETS,
},
[OCELOT_STAT_TX_UNICAST] = {
.name = "tx_unicast",
.reg = SYS_COUNT_TX_UNICAST,
},
[OCELOT_STAT_TX_MULTICAST] = {
.name = "tx_multicast",
.reg = SYS_COUNT_TX_MULTICAST,
},
[OCELOT_STAT_TX_BROADCAST] = {
.name = "tx_broadcast",
.reg = SYS_COUNT_TX_BROADCAST,
},
[OCELOT_STAT_TX_COLLISION] = {
.name = "tx_collision",
.reg = SYS_COUNT_TX_COLLISION,
},
[OCELOT_STAT_TX_DROPS] = {
.name = "tx_drops",
.reg = SYS_COUNT_TX_DROPS,
},
[OCELOT_STAT_TX_PAUSE] = {
.name = "tx_pause",
.reg = SYS_COUNT_TX_PAUSE,
},
[OCELOT_STAT_TX_64] = {
.name = "tx_frames_below_65_octets",
.reg = SYS_COUNT_TX_64,
},
[OCELOT_STAT_TX_65_127] = {
.name = "tx_frames_65_to_127_octets",
.reg = SYS_COUNT_TX_65_127,
},
[OCELOT_STAT_TX_128_255] = {
.name = "tx_frames_128_255_octets",
.reg = SYS_COUNT_TX_128_255,
},
[OCELOT_STAT_TX_256_511] = {
.name = "tx_frames_256_511_octets",
.reg = SYS_COUNT_TX_256_511,
},
[OCELOT_STAT_TX_512_1023] = {
.name = "tx_frames_512_1023_octets",
.reg = SYS_COUNT_TX_512_1023,
},
[OCELOT_STAT_TX_1024_1526] = {
.name = "tx_frames_1024_1526_octets",
.reg = SYS_COUNT_TX_1024_1526,
},
[OCELOT_STAT_TX_1527_MAX] = {
.name = "tx_frames_over_1526_octets",
.reg = SYS_COUNT_TX_1527_MAX,
},
[OCELOT_STAT_TX_YELLOW_PRIO_0] = {
.name = "tx_yellow_prio_0",
.reg = SYS_COUNT_TX_YELLOW_PRIO_0,
},
[OCELOT_STAT_TX_YELLOW_PRIO_1] = {
.name = "tx_yellow_prio_1",
.reg = SYS_COUNT_TX_YELLOW_PRIO_1,
},
[OCELOT_STAT_TX_YELLOW_PRIO_2] = {
.name = "tx_yellow_prio_2",
.reg = SYS_COUNT_TX_YELLOW_PRIO_2,
},
[OCELOT_STAT_TX_YELLOW_PRIO_3] = {
.name = "tx_yellow_prio_3",
.reg = SYS_COUNT_TX_YELLOW_PRIO_3,
},
[OCELOT_STAT_TX_YELLOW_PRIO_4] = {
.name = "tx_yellow_prio_4",
.reg = SYS_COUNT_TX_YELLOW_PRIO_4,
},
[OCELOT_STAT_TX_YELLOW_PRIO_5] = {
.name = "tx_yellow_prio_5",
.reg = SYS_COUNT_TX_YELLOW_PRIO_5,
},
[OCELOT_STAT_TX_YELLOW_PRIO_6] = {
.name = "tx_yellow_prio_6",
.reg = SYS_COUNT_TX_YELLOW_PRIO_6,
},
[OCELOT_STAT_TX_YELLOW_PRIO_7] = {
.name = "tx_yellow_prio_7",
.reg = SYS_COUNT_TX_YELLOW_PRIO_7,
},
[OCELOT_STAT_TX_GREEN_PRIO_0] = {
.name = "tx_green_prio_0",
.reg = SYS_COUNT_TX_GREEN_PRIO_0,
},
[OCELOT_STAT_TX_GREEN_PRIO_1] = {
.name = "tx_green_prio_1",
.reg = SYS_COUNT_TX_GREEN_PRIO_1,
},
[OCELOT_STAT_TX_GREEN_PRIO_2] = {
.name = "tx_green_prio_2",
.reg = SYS_COUNT_TX_GREEN_PRIO_2,
},
[OCELOT_STAT_TX_GREEN_PRIO_3] = {
.name = "tx_green_prio_3",
.reg = SYS_COUNT_TX_GREEN_PRIO_3,
},
[OCELOT_STAT_TX_GREEN_PRIO_4] = {
.name = "tx_green_prio_4",
.reg = SYS_COUNT_TX_GREEN_PRIO_4,
},
[OCELOT_STAT_TX_GREEN_PRIO_5] = {
.name = "tx_green_prio_5",
.reg = SYS_COUNT_TX_GREEN_PRIO_5,
},
[OCELOT_STAT_TX_GREEN_PRIO_6] = {
.name = "tx_green_prio_6",
.reg = SYS_COUNT_TX_GREEN_PRIO_6,
},
[OCELOT_STAT_TX_GREEN_PRIO_7] = {
.name = "tx_green_prio_7",
.reg = SYS_COUNT_TX_GREEN_PRIO_7,
},
[OCELOT_STAT_TX_AGED] = {
.name = "tx_aged",
.reg = SYS_COUNT_TX_AGING,
},
[OCELOT_STAT_DROP_LOCAL] = {
.name = "drop_local",
.reg = SYS_COUNT_DROP_LOCAL,
},
[OCELOT_STAT_DROP_TAIL] = {
.name = "drop_tail",
.reg = SYS_COUNT_DROP_TAIL,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_0] = {
.name = "drop_yellow_prio_0",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_0,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_1] = {
.name = "drop_yellow_prio_1",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_1,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_2] = {
.name = "drop_yellow_prio_2",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_2,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_3] = {
.name = "drop_yellow_prio_3",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_3,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_4] = {
.name = "drop_yellow_prio_4",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_4,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_5] = {
.name = "drop_yellow_prio_5",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_5,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_6] = {
.name = "drop_yellow_prio_6",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_6,
},
[OCELOT_STAT_DROP_YELLOW_PRIO_7] = {
.name = "drop_yellow_prio_7",
.reg = SYS_COUNT_DROP_YELLOW_PRIO_7,
},
[OCELOT_STAT_DROP_GREEN_PRIO_0] = {
.name = "drop_green_prio_0",
.reg = SYS_COUNT_DROP_GREEN_PRIO_0,
},
[OCELOT_STAT_DROP_GREEN_PRIO_1] = {
.name = "drop_green_prio_1",
.reg = SYS_COUNT_DROP_GREEN_PRIO_1,
},
[OCELOT_STAT_DROP_GREEN_PRIO_2] = {
.name = "drop_green_prio_2",
.reg = SYS_COUNT_DROP_GREEN_PRIO_2,
},
[OCELOT_STAT_DROP_GREEN_PRIO_3] = {
.name = "drop_green_prio_3",
.reg = SYS_COUNT_DROP_GREEN_PRIO_3,
},
[OCELOT_STAT_DROP_GREEN_PRIO_4] = {
.name = "drop_green_prio_4",
.reg = SYS_COUNT_DROP_GREEN_PRIO_4,
},
[OCELOT_STAT_DROP_GREEN_PRIO_5] = {
.name = "drop_green_prio_5",
.reg = SYS_COUNT_DROP_GREEN_PRIO_5,
},
[OCELOT_STAT_DROP_GREEN_PRIO_6] = {
.name = "drop_green_prio_6",
.reg = SYS_COUNT_DROP_GREEN_PRIO_6,
},
[OCELOT_STAT_DROP_GREEN_PRIO_7] = {
.name = "drop_green_prio_7",
.reg = SYS_COUNT_DROP_GREEN_PRIO_7,
},
}; };
static void ocelot_pll5_init(struct ocelot *ocelot) static void ocelot_pll5_init(struct ocelot *ocelot)
......
...@@ -242,7 +242,7 @@ const u32 vsc7514_sys_regmap[] = { ...@@ -242,7 +242,7 @@ const u32 vsc7514_sys_regmap[] = {
REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00016c), REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00016c),
REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000170), REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000170),
REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000174), REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000174),
REG(SYS_COUNT_TX_AGING, 0x000178), REG(SYS_COUNT_TX_AGED, 0x000178),
REG(SYS_COUNT_DROP_LOCAL, 0x000200), REG(SYS_COUNT_DROP_LOCAL, 0x000200),
REG(SYS_COUNT_DROP_TAIL, 0x000204), REG(SYS_COUNT_DROP_TAIL, 0x000204),
REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000208), REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000208),
...@@ -283,7 +283,6 @@ const u32 vsc7514_sys_regmap[] = { ...@@ -283,7 +283,6 @@ const u32 vsc7514_sys_regmap[] = {
REG(SYS_MMGT_FAST, 0x0006a0), REG(SYS_MMGT_FAST, 0x0006a0),
REG(SYS_EVENTS_DIF, 0x0006a4), REG(SYS_EVENTS_DIF, 0x0006a4),
REG(SYS_EVENTS_CORE, 0x0006b4), REG(SYS_EVENTS_CORE, 0x0006b4),
REG(SYS_CNT, 0x000000),
REG(SYS_PTP_STATUS, 0x0006b8), REG(SYS_PTP_STATUS, 0x0006b8),
REG(SYS_PTP_TXSTAMP, 0x0006bc), REG(SYS_PTP_TXSTAMP, 0x0006bc),
REG(SYS_PTP_NXT, 0x0006c0), REG(SYS_PTP_NXT, 0x0006c0),
......
...@@ -392,7 +392,7 @@ enum ocelot_reg { ...@@ -392,7 +392,7 @@ enum ocelot_reg {
SYS_COUNT_TX_GREEN_PRIO_5, SYS_COUNT_TX_GREEN_PRIO_5,
SYS_COUNT_TX_GREEN_PRIO_6, SYS_COUNT_TX_GREEN_PRIO_6,
SYS_COUNT_TX_GREEN_PRIO_7, SYS_COUNT_TX_GREEN_PRIO_7,
SYS_COUNT_TX_AGING, SYS_COUNT_TX_AGED,
SYS_COUNT_DROP_LOCAL, SYS_COUNT_DROP_LOCAL,
SYS_COUNT_DROP_TAIL, SYS_COUNT_DROP_TAIL,
SYS_COUNT_DROP_YELLOW_PRIO_0, SYS_COUNT_DROP_YELLOW_PRIO_0,
...@@ -411,6 +411,10 @@ enum ocelot_reg { ...@@ -411,6 +411,10 @@ enum ocelot_reg {
SYS_COUNT_DROP_GREEN_PRIO_5, SYS_COUNT_DROP_GREEN_PRIO_5,
SYS_COUNT_DROP_GREEN_PRIO_6, SYS_COUNT_DROP_GREEN_PRIO_6,
SYS_COUNT_DROP_GREEN_PRIO_7, SYS_COUNT_DROP_GREEN_PRIO_7,
SYS_COUNT_SF_MATCHING_FRAMES,
SYS_COUNT_SF_NOT_PASSING_FRAMES,
SYS_COUNT_SF_NOT_PASSING_SDU,
SYS_COUNT_SF_RED_FRAMES,
SYS_RESET_CFG, SYS_RESET_CFG,
SYS_SR_ETYPE_CFG, SYS_SR_ETYPE_CFG,
SYS_VLAN_ETYPE_CFG, SYS_VLAN_ETYPE_CFG,
...@@ -433,7 +437,6 @@ enum ocelot_reg { ...@@ -433,7 +437,6 @@ enum ocelot_reg {
SYS_MMGT_FAST, SYS_MMGT_FAST,
SYS_EVENTS_DIF, SYS_EVENTS_DIF,
SYS_EVENTS_CORE, SYS_EVENTS_CORE,
SYS_CNT,
SYS_PTP_STATUS, SYS_PTP_STATUS,
SYS_PTP_TXSTAMP, SYS_PTP_TXSTAMP,
SYS_PTP_NXT, SYS_PTP_NXT,
...@@ -695,6 +698,112 @@ struct ocelot_stat_layout { ...@@ -695,6 +698,112 @@ struct ocelot_stat_layout {
char name[ETH_GSTRING_LEN]; char name[ETH_GSTRING_LEN];
}; };
/* 32-bit counter checked for wraparound by ocelot_port_update_stats()
* and copied to ocelot->stats.
*/
#define OCELOT_STAT(kind) \
[OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind }
/* Same as above, except also exported to ethtool -S. Standard counters should
* only be exposed to more specific interfaces rather than by their string name.
*/
#define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \
[OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name }
#define OCELOT_COMMON_STATS \
OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \
OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \
OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \
OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \
OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \
OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \
OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \
OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \
OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \
OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \
OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \
OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \
OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \
OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \
OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \
OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \
OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \
OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \
OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \
OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \
OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \
OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \
OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \
OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \
OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \
OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \
OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \
OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \
OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \
OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \
OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \
OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \
OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \
OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \
OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \
OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \
OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \
OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \
OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \
OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \
OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \
OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \
OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \
OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7")
struct ocelot_stats_region { struct ocelot_stats_region {
struct list_head node; struct list_head node;
u32 base; u32 base;
...@@ -726,6 +835,7 @@ struct ocelot_ops { ...@@ -726,6 +835,7 @@ struct ocelot_ops {
struct flow_stats *stats); struct flow_stats *stats);
void (*cut_through_fwd)(struct ocelot *ocelot); void (*cut_through_fwd)(struct ocelot *ocelot);
void (*tas_clock_adjust)(struct ocelot *ocelot); void (*tas_clock_adjust)(struct ocelot *ocelot);
void (*update_stats)(struct ocelot *ocelot);
}; };
struct ocelot_vcap_policer { struct ocelot_vcap_policer {
...@@ -763,6 +873,8 @@ struct ocelot_psfp_list { ...@@ -763,6 +873,8 @@ struct ocelot_psfp_list {
struct list_head stream_list; struct list_head stream_list;
struct list_head sfi_list; struct list_head sfi_list;
struct list_head sgi_list; struct list_head sgi_list;
/* Serialize access to the lists */
struct mutex lock;
}; };
enum ocelot_sb { enum ocelot_sb {
...@@ -898,12 +1010,15 @@ struct ocelot { ...@@ -898,12 +1010,15 @@ struct ocelot {
struct ocelot_psfp_list psfp; struct ocelot_psfp_list psfp;
/* Workqueue to check statistics for overflow with its lock */ /* Workqueue to check statistics for overflow */
spinlock_t stats_lock;
u64 *stats;
struct delayed_work stats_work; struct delayed_work stats_work;
struct workqueue_struct *stats_queue; struct workqueue_struct *stats_queue;
/* Lock for serializing access to the statistics array */
spinlock_t stats_lock;
u64 *stats;
/* Lock for serializing indirect access to STAT_VIEW registers */
struct mutex stat_view_lock;
/* Lock for serializing access to the MAC table */ /* Lock for serializing access to the MAC table */
struct mutex mact_lock; struct mutex mact_lock;
/* Lock for serializing forwarding domain changes */ /* Lock for serializing forwarding domain changes */
...@@ -1034,6 +1149,19 @@ u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port); ...@@ -1034,6 +1149,19 @@ u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port);
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data); void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset); int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset);
void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
struct rtnl_link_stats64 *stats);
void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
struct ethtool_pause_stats *pause_stats);
void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges);
void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats);
void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_mac_stats *mac_stats);
void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_phy_stats *phy_stats);
int ocelot_get_ts_info(struct ocelot *ocelot, int port, int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info); struct ethtool_ts_info *info);
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs); void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
......
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