@@ -16,6 +16,45 @@
#include "ksz_common.h"
#include "lan937x_dev.h"
+const struct mib_names lan937x_mib_names[] = {
+ { 0x00, "rx_hi" },
+ { 0x01, "rx_undersize" },
+ { 0x02, "rx_fragments" },
+ { 0x03, "rx_oversize" },
+ { 0x04, "rx_jabbers" },
+ { 0x05, "rx_symbol_err" },
+ { 0x06, "rx_crc_err" },
+ { 0x07, "rx_align_err" },
+ { 0x08, "rx_mac_ctrl" },
+ { 0x09, "rx_pause" },
+ { 0x0A, "rx_bcast" },
+ { 0x0B, "rx_mcast" },
+ { 0x0C, "rx_ucast" },
+ { 0x0D, "rx_64_or_less" },
+ { 0x0E, "rx_65_127" },
+ { 0x0F, "rx_128_255" },
+ { 0x10, "rx_256_511" },
+ { 0x11, "rx_512_1023" },
+ { 0x12, "rx_1024_1522" },
+ { 0x13, "rx_1523_2000" },
+ { 0x14, "rx_2001" },
+ { 0x15, "tx_hi" },
+ { 0x16, "tx_late_col" },
+ { 0x17, "tx_pause" },
+ { 0x18, "tx_bcast" },
+ { 0x19, "tx_mcast" },
+ { 0x1A, "tx_ucast" },
+ { 0x1B, "tx_deferred" },
+ { 0x1C, "tx_total_col" },
+ { 0x1D, "tx_exc_col" },
+ { 0x1E, "tx_single_col" },
+ { 0x1F, "tx_mult_col" },
+ { 0x80, "rx_total" },
+ { 0x81, "tx_total" },
+ { 0x82, "rx_discards" },
+ { 0x83, "tx_discards" },
+};
+
int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
{
return regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
@@ -93,6 +132,53 @@ static void lan937x_flush_dyn_mac_table(struct ksz_device *dev, int port)
}
}
+static void lan937x_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
+ u64 *cnt)
+{
+ unsigned int val;
+ u32 data;
+ int ret;
+
+ /* Enable MIB Counter read */
+ data = MIB_COUNTER_READ;
+ data |= (addr << MIB_COUNTER_INDEX_S);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT, data);
+
+ ret = regmap_read_poll_timeout(dev->regmap[2],
+ PORT_CTRL_ADDR(port,
+ REG_PORT_MIB_CTRL_STAT),
+ val, !(val & MIB_COUNTER_READ),
+ 10, 1000);
+ if (ret) {
+ dev_err(dev->dev, "Failed to get MIB\n");
+ return;
+ }
+
+ /* count resets upon read */
+ lan937x_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+ *cnt += data;
+}
+
+void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+ u64 *dropped, u64 *cnt)
+{
+ addr = lan937x_mib_names[addr].index;
+ lan937x_r_mib_cnt(dev, port, addr, cnt);
+}
+
+static void lan937x_port_init_cnt(struct ksz_device *dev, int port)
+{
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+
+ /* flush all enabled port MIB counters */
+ mutex_lock(&mib->cnt_mutex);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT,
+ MIB_COUNTER_FLUSH_FREEZE);
+ ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT, 0);
+ mutex_unlock(&mib->cnt_mutex);
+}
+
int lan937x_reset_switch(struct ksz_device *dev)
{
u32 data32;
@@ -573,7 +659,7 @@ static int lan937x_mdio_register(struct ksz_device *dev)
static int lan937x_switch_init(struct ksz_device *dev)
{
- int ret;
+ int i, ret;
dev->ds->ops = &lan937x_switch_ops;
@@ -584,12 +670,27 @@ static int lan937x_switch_init(struct ksz_device *dev)
dev->port_mask = (1 << dev->port_cnt) - 1;
+ dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
+ dev->mib_cnt = ARRAY_SIZE(lan937x_mib_names);
+
dev->ports = devm_kzalloc(dev->dev,
dev->port_cnt * sizeof(struct ksz_port),
GFP_KERNEL);
if (!dev->ports)
return -ENOMEM;
+ for (i = 0; i < dev->port_cnt; i++) {
+ spin_lock_init(&dev->ports[i].mib.stats64_lock);
+ mutex_init(&dev->ports[i].mib.cnt_mutex);
+ dev->ports[i].mib.counters =
+ devm_kzalloc(dev->dev,
+ sizeof(u64) * (dev->mib_cnt + 1),
+ GFP_KERNEL);
+
+ if (!dev->ports[i].mib.counters)
+ return -ENOMEM;
+ }
+
/* set the real number of ports */
dev->ds->num_ports = dev->port_cnt;
return 0;
@@ -626,6 +727,10 @@ const struct ksz_dev_ops lan937x_dev_ops = {
.cfg_port_member = lan937x_cfg_port_member,
.flush_dyn_mac_table = lan937x_flush_dyn_mac_table,
.port_setup = lan937x_port_setup,
+ .r_mib_cnt = lan937x_r_mib_cnt,
+ .r_mib_pkt = lan937x_r_mib_pkt,
+ .port_init_cnt = lan937x_port_init_cnt,
+ .r_mib_stat64 = ksz_r_mib_stats64,
.shutdown = lan937x_reset_switch,
.detect = lan937x_switch_detect,
.init = lan937x_init,
@@ -38,8 +38,55 @@ void lan937x_config_interface(struct ksz_device *dev, int port,
bool tx_pause, bool rx_pause);
void lan937x_mac_config(struct ksz_device *dev, int port,
phy_interface_t interface);
+void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+ u64 *dropped, u64 *cnt);
+
+struct mib_names {
+ int index;
+ char string[ETH_GSTRING_LEN];
+};
+
+enum lan937x_mib_list {
+ lan937x_mib_rx_hi_pri_byte = 0,
+ lan937x_mib_rx_undersize,
+ lan937x_mib_rx_fragments,
+ lan937x_mib_rx_oversize,
+ lan937x_mib_rx_jabbers,
+ lan937x_mib_rx_sym_err,
+ lan937x_mib_rx_crc_err,
+ lan937x_mib_rx_align_err,
+ lan937x_mib_rx_mac_ctrl,
+ lan937x_mib_rx_pause,
+ lan937x_mib_rx_bcast,
+ lan937x_mib_rx_mcast,
+ lan937x_mib_rx_ucast,
+ lan937x_mib_rx_64_or_less,
+ lan937x_mib_rx_65_127,
+ lan937x_mib_rx_128_255,
+ lan937x_mib_rx_256_511,
+ lan937x_mib_rx_512_1023,
+ lan937x_mib_rx_1024_1522,
+ lan937x_mib_rx_1523_2000,
+ lan937x_mib_rx_2001,
+ lan937x_mib_tx_hi_pri_byte,
+ lan937x_mib_tx_late_col,
+ lan937x_mib_tx_pause,
+ lan937x_mib_tx_bcast,
+ lan937x_mib_tx_mcast,
+ lan937x_mib_tx_ucast,
+ lan937x_mib_tx_deferred,
+ lan937x_mib_tx_total_col,
+ lan937x_mib_tx_exc_col,
+ lan937x_mib_tx_single_col,
+ lan937x_mib_tx_mult_col,
+ lan937x_mib_rx_total,
+ lan937x_mib_tx_total,
+ lan937x_mib_rx_discard,
+ lan937x_mib_tx_discard,
+};
extern const struct dsa_switch_ops lan937x_switch_ops;
extern const struct ksz_dev_ops lan937x_dev_ops;
+extern const struct mib_names lan937x_mib_names[];
#endif
@@ -45,6 +45,20 @@ static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
return lan937x_internal_phy_write(dev, addr, reg, val);
}
+static void lan937x_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ u8 *buf)
+{
+ struct ksz_device *dev = ds->priv;
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < dev->mib_cnt; i++)
+ memcpy(buf + i * ETH_GSTRING_LEN, lan937x_mib_names[i].string,
+ ETH_GSTRING_LEN);
+}
+
static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
@@ -215,6 +229,8 @@ static int lan937x_setup(struct dsa_switch *ds)
/* start switch */
lan937x_cfg(dev, REG_SW_OPERATION, SW_START, true);
+ ksz_init_mib_timer(dev);
+
return 0;
}
@@ -320,12 +336,105 @@ static void lan937x_phylink_get_caps(struct dsa_switch *ds, int port,
}
}
+static void lan937x_get_eth_phy_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *cnt;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ cnt = &mib->counters[lan937x_mib_rx_sym_err];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_rx_sym_err, NULL, cnt);
+
+ phy_stats->SymbolErrorDuringCarrier = *cnt;
+
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+static void lan937x_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *ctr = mib->counters;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ while (mib->cnt_ptr < dev->mib_cnt) {
+ lan937x_r_mib_pkt(dev, port, mib->cnt_ptr,
+ NULL, &mib->counters[mib->cnt_ptr]);
+ ++mib->cnt_ptr;
+ }
+
+ mac_stats->FramesTransmittedOK = ctr[lan937x_mib_tx_mcast] +
+ ctr[lan937x_mib_tx_bcast] +
+ ctr[lan937x_mib_tx_ucast] +
+ ctr[lan937x_mib_tx_pause];
+
+ mac_stats->SingleCollisionFrames = ctr[lan937x_mib_tx_single_col];
+ mac_stats->MultipleCollisionFrames = ctr[lan937x_mib_tx_mult_col];
+
+ mac_stats->FramesReceivedOK = ctr[lan937x_mib_rx_mcast] +
+ ctr[lan937x_mib_rx_bcast] +
+ ctr[lan937x_mib_rx_ucast] +
+ ctr[lan937x_mib_rx_pause];
+
+ mac_stats->FrameCheckSequenceErrors = ctr[lan937x_mib_rx_crc_err];
+ mac_stats->AlignmentErrors = ctr[lan937x_mib_rx_align_err];
+ mac_stats->OctetsTransmittedOK = ctr[lan937x_mib_tx_total];
+ mac_stats->FramesWithDeferredXmissions = ctr[lan937x_mib_tx_deferred];
+ mac_stats->LateCollisions = ctr[lan937x_mib_tx_late_col];
+ mac_stats->FramesAbortedDueToXSColls = ctr[lan937x_mib_tx_exc_col];
+ mac_stats->FramesLostDueToIntMACXmitError = ctr[lan937x_mib_tx_discard];
+
+ mac_stats->OctetsReceivedOK = ctr[lan937x_mib_rx_total];
+ mac_stats->FramesLostDueToIntMACRcvError = ctr[lan937x_mib_rx_discard];
+ mac_stats->MulticastFramesXmittedOK = ctr[lan937x_mib_tx_mcast];
+ mac_stats->BroadcastFramesXmittedOK = ctr[lan937x_mib_tx_bcast];
+
+ mac_stats->MulticastFramesReceivedOK = ctr[lan937x_mib_rx_mcast];
+ mac_stats->BroadcastFramesReceivedOK = ctr[lan937x_mib_rx_bcast];
+ mac_stats->InRangeLengthErrors = ctr[lan937x_mib_rx_fragments];
+
+ mib->cnt_ptr = 0;
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+static void lan937x_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_sts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *cnt;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ cnt = &mib->counters[lan937x_mib_rx_pause];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_rx_pause, NULL, cnt);
+ ctrl_sts->MACControlFramesReceived = *cnt;
+
+ cnt = &mib->counters[lan937x_mib_tx_pause];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_tx_pause, NULL, cnt);
+ ctrl_sts->MACControlFramesTransmitted = *cnt;
+
+ mutex_unlock(&mib->cnt_mutex);
+}
+
const struct dsa_switch_ops lan937x_switch_ops = {
.get_tag_protocol = lan937x_get_tag_protocol,
.setup = lan937x_setup,
.phy_read = lan937x_phy_read16,
.phy_write = lan937x_phy_write16,
.port_enable = ksz_enable_port,
+ .get_strings = lan937x_get_strings,
+ .get_ethtool_stats = ksz_get_ethtool_stats,
+ .get_sset_count = ksz_sset_count,
+ .get_eth_ctrl_stats = lan937x_get_eth_ctrl_stats,
+ .get_eth_mac_stats = lan937x_get_eth_mac_stats,
+ .get_eth_phy_stats = lan937x_get_eth_phy_stats,
+ .get_stats64 = ksz_get_stats64,
.port_bridge_join = ksz_port_bridge_join,
.port_bridge_leave = ksz_port_bridge_leave,
.port_stp_state_set = lan937x_port_stp_state_set,